Merge pull request #13225 from Kilo-Org/busy-computer

fix: remove agent requirements feature
This commit is contained in:
Marius
2026-08-19 12:39:42 +02:00
committed by GitHub
73 changed files with 78 additions and 3315 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Remove the experimental agent requirements check and its configuration flag.
+1 -58
View File
@@ -9,62 +9,6 @@ const Color = Schema.Union([
Schema.Literals(["primary", "secondary", "accent", "success", "warning", "error", "info"]),
])
// kilocode_change start - agent skill/MCP/VS Code extension requirements schema
const RequirementID = Schema.String.check(
Schema.isMinLength(1),
Schema.isMaxLength(128),
Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$/),
)
const RequirementName = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(128), Schema.isPattern(/\S/))
export const VSCodeExtension = Schema.Struct({
name: RequirementName,
id: RequirementID,
})
export type VSCodeExtension = Schema.Schema.Type<typeof VSCodeExtension>
const RequirementGroup = Schema.mutable(Schema.Array(RequirementName)).check(
Schema.isMinLength(1),
Schema.isMaxLength(20),
)
const VSCodeExtensions = Schema.mutable(Schema.Array(VSCodeExtension)).check(
Schema.isMinLength(1),
Schema.isMaxLength(20),
)
export const Requirements = Schema.Struct({
skills: Schema.optional(RequirementGroup),
mcps: Schema.optional(RequirementGroup),
vscode_extensions: Schema.optional(VSCodeExtensions),
}).check(
Schema.makeFilter((input) => {
const issues: Schema.FilterIssue[] = []
if (!input.skills && !input.mcps && !input.vscode_extensions) {
issues.push({ path: [], issue: "At least one requirement group is required" })
}
for (const group of ["skills", "mcps"] as const) {
const seen = new Set<string>()
for (const [index, value] of (input[group] ?? []).entries()) {
if (seen.has(value)) issues.push({ path: [group, index], issue: `Duplicate ${group} requirement` })
seen.add(value)
}
}
const seen = new Set<string>()
for (const [index, extension] of (input.vscode_extensions ?? []).entries()) {
if (seen.has(extension.id)) {
issues.push({ path: ["vscode_extensions", index, "id"], issue: "Duplicate vscode_extensions requirement" })
}
seen.add(extension.id)
}
return issues
}),
)
export type Requirements = Schema.Schema.Type<typeof Requirements>
// kilocode_change end
const AgentSchema = Schema.StructWithRest(
Schema.Struct({
model: Schema.optional(Schema.NullOr(Schema.String)), // kilocode_change - nullable for delete sentinel
@@ -108,7 +52,6 @@ const AgentSchema = Schema.StructWithRest(
// kilocode_change end
maxSteps: Schema.optional(PositiveInt).annotate({ description: "@deprecated Use 'steps' field instead." }),
permission: Schema.optional(ConfigPermissionV1.Info),
requirements: Schema.optional(Requirements), // kilocode_change
}),
[Schema.Record(Schema.String, Schema.Any)],
)
@@ -132,7 +75,7 @@ const KNOWN_KEYS = new Set([
"permission",
"disable",
"tools",
"requirements", // kilocode_change
"requirements", // kilocode_change - ignore declarations from removed agent requirements feature
])
const normalize = (agent: Schema.Schema.Type<typeof AgentSchema>): Schema.Schema.Type<typeof AgentSchema> => {
-3
View File
@@ -301,9 +301,6 @@ export const Info = Schema.Struct({
image_generation_model: Schema.optional(Schema.String).annotate({
description: "Model ID to use for image generation (default: openrouter/auto)",
}),
agent_requirements: Schema.optional(Schema.Boolean).annotate({
description: "Require declared agent skills, MCPs, and VS Code extensions before VS Code prompts can run",
}),
native_notebook_tools: Schema.optional(Schema.Boolean).annotate({
description: "Enable native tools for reading, editing, and executing VS Code notebooks",
}),
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a093b9658cc15d0789da5afebde7163ec51e743af59975e2a031aed3e9144d56
size 22336
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b20e8e50b583cc6a884e08648a9f1efcdfc2a9561b97145811ebc9c24d244fd
size 23100
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b24b0d2694d2eeec0ea4cd827bd3a61bb47150605b838b43e4f422c9b960f0e
size 29744
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd27610a2518018c16e4227728221ed9bf67a7d3c8ab207b2835dc60964ec63d
size 33323
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d8ad46cc736472c6a2a46bb80e6622333f49960e74dcb6782d35aa72fa32378c
size 23725
-44
View File
@@ -51,7 +51,6 @@ import {
import { GitOps } from "./agent-manager/GitOps"
import { GitStatsPoller, type LocalStats } from "./agent-manager/GitStatsPoller"
import { createMarketplaceRemover, removeMcp } from "./kilo-provider/remove-config-item"
import { AgentRequirementsController } from "./kilo-provider/agent-requirements-controller"
import type { RemoteStatusService } from "./services/RemoteStatusService"
import { resolveProjectDirectory } from "./project-directory"
import { seedSessionStatuses } from "./session-status"
@@ -467,7 +466,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
private diffVirtualProvider: import("./DiffVirtualProvider").DiffVirtualProvider | undefined
private remoteService: RemoteStatusService | null = null
private unsubscribeRemote: (() => void) | null = null
private readonly requirements: AgentRequirementsController
constructor(
private readonly extensionUri: vscode.Uri,
@@ -480,24 +478,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.unsubscribeSandboxPreference = this.connectionService.sandboxPreference?.onChange(() => {
if (this.connectionState === "connected") void this.fetchAndSendSandboxDefault()
})
this.requirements = new AgentRequirementsController({
post: (msg) => this.postMessage(msg),
client: () => this.client,
connected: () => this.connectionState === "connected",
generation: () => this.connectionGeneration,
root: () => this.getRootDirectory(),
folders: () => vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath),
project: () => this.projectDirectory,
sessions: () => this.sessionDirectories,
worktrees: this.opts.worktreeDirectories,
extension: (id) => vscode.extensions.getExtension(id),
subscribe:
typeof vscode.extensions.onDidChange === "function"
? (listener) => vscode.extensions.onDidChange(listener)
: undefined,
error: getErrorMessage,
})
TelemetryProxy.getInstance().setProvider(this)
}
@@ -575,7 +555,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.cachedConfigMessage = null
this.postMessage({ type: "workspaceDirectoryChanged", directory: directory ?? "" })
this.postMessage({ type: "configBindingExpired", reason: "project-changed" })
this.requirements.clear()
}
public setDiffVirtualProvider(provider: import("./DiffVirtualProvider").DiffVirtualProvider): void {
@@ -663,7 +642,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.cachedAgentsMessage = null
this.cachedConfigMessage = null
await Promise.all([this.fetchAndSendAgents(), this.fetchAndSendConfig()])
this.requirements.clear()
},
storage: this.extensionContext?.globalStorageUri,
}
@@ -840,7 +818,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
const current = this.sessionDirectories.get(sessionId) ?? this.getRootDirectory()
this.aborts.preserve(sessionId, this.sessionStatusMap.get(sessionId), current)
this.sessionDirectories.set(sessionId, directory)
this.requirements.clear()
if (this.connectionState === "connected") void this.fetchAndSendSandboxStatus(sessionId)
}
@@ -848,7 +825,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
const current = this.sessionDirectories.get(sessionId) ?? this.getRootDirectory()
this.aborts.preserve(sessionId, this.sessionStatusMap.get(sessionId), current)
this.sessionDirectories.delete(sessionId)
this.requirements.clear()
if (this.connectionState === "connected") void this.fetchAndSendSandboxStatus(sessionId)
}
@@ -1275,16 +1251,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
case "requestSkills":
this.fetchAndSendSkills().catch((e) => console.error("[Kilo New] fetchAndSendSkills failed:", e))
break
case "requestAgentRequirements":
this.requirements
.fetch({
agent: message.agent,
directory: message.directory,
sessionID: message.sessionID,
force: message.force === true,
})
.catch((e) => console.error("[Kilo New] fetchAndSendAgentRequirements failed:", e))
break
case "requestCommands":
this.fetchAndSendCommands().catch((e) => console.error("[Kilo New] fetchAndSendCommands failed:", e))
break
@@ -1967,7 +1933,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
})
.catch((e: unknown) => console.warn("[Kilo New] KiloProvider: getSession failed (non-critical):", e))
this.postMessage({ type: "workspaceDirectoryChanged", directory: this.getWorkspaceDirectory(sessionID) })
this.requirements.clear()
this.client.session
.status({ directory: dir })
.then((r) => {
@@ -2661,7 +2626,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.cachedSkillsMessage = null
this.cachedCommandsMessage = null
await Promise.all([this.fetchAndSendSkills(), this.fetchAndSendCommands()])
this.requirements.clear()
return true
}
@@ -2678,7 +2642,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
}
this.cachedAgentsMessage = null
await this.fetchAndSendAgents()
this.requirements.clear()
}
private async handleRemoveMcp(name: string): Promise<void> {
@@ -2690,7 +2653,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
private async refreshMcpStatus(): Promise<void> {
await this.fetchAndSendMcpStatus()
this.requirements.clear()
}
private async fetchAndSendMcpStatus(): Promise<void> {
@@ -3277,7 +3239,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
settings: this.configSettings(),
features: configFeatures(snapshot.effective),
})
this.requirements.clear()
await Promise.all([
refreshProviders ? this.fetchAndSendProviders() : Promise.resolve(),
refreshAgents ? this.fetchAndSendAgents() : Promise.resolve(),
@@ -3740,7 +3701,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
}
parts.push({ type: "text", text, metadata: review ? reviewMetadata(review) : undefined })
await this.requirements.assertAgentRequirements(agent, dir)
const editorContext = await this.gatherEditorContext(dir)
if (draftID && this.closedDrafts.delete(draftID)) {
for (const [k, v] of this.draftSessions) if (v.sid === sid) this.draftSessions.delete(k)
@@ -3835,7 +3795,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
source: f.source,
}))
await this.requirements.assertAgentRequirements(agent, dir)
await this.checkpoints.get(sid)
await runWithMessageConfirmation(this.confirmations, messageID, "KiloProvider: Command request", () =>
this.withRetry(
@@ -4205,7 +4164,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
/** Re-fetch all server-side state after an auth change. */
private async reloadAfterAuthChange(): Promise<void> {
this.requirements.clear()
await this.fetchAndSendConfig()
await Promise.all([
this.fetchAndSendProviders(),
@@ -4511,7 +4469,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
// Fetch and push the updated config + refresh agents and providers so the
// Settings panel and mode/model pickers reflect the change.
if (event.type === "global.config.updated") {
this.requirements.clear()
void Promise.all([this.fetchAndSendConfigUpdated(), this.fetchAndSendAgents(), this.fetchAndSendProviders()])
return
}
@@ -5151,7 +5108,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.anacondaDesktop.dispose()
this.aborts.clear()
this.sessionStatusMap.clear()
this.requirements.dispose()
this.ignoreController?.dispose()
this.chatAutocomplete?.dispose()
disposeGitChangesTarget()
@@ -1,198 +0,0 @@
import type { KiloClient } from "@kilocode/sdk/v2/client"
import {
applyVSCodeExtensionRequirements,
requirementDirectory,
requirementKey,
type BackendAgentRequirementResult,
type HostAgentRequirementResult,
} from "./agent-requirements"
type AgentRequirementsClient = {
agentRequirements: (
parameters: { agent: string; directory: string },
options: { throwOnError: true },
) => Promise<{ data: BackendAgentRequirementResult }>
}
type LoadedMessage = {
type: "agentRequirementsLoaded"
result: HostAgentRequirementResult
}
type InvalidatedMessage = {
type: "agentRequirementsInvalidated"
}
type Disposable = {
dispose(): void
}
export type AgentRequirementsRequest = {
agent: string
directory: string
sessionID?: string
force?: boolean
}
export type AgentRequirementsControllerOptions = {
post: (message: LoadedMessage | InvalidatedMessage) => void
client: () => KiloClient | null
connected: () => boolean
generation: () => number
root: () => string
folders: () => readonly string[] | undefined
project: () => string | null | undefined
sessions: () => ReadonlyMap<string, string>
worktrees?: () => readonly string[]
extension: (id: string) => unknown
subscribe?: (listener: () => void) => Disposable
error: (error: unknown) => string
}
export class AgentRequirementsController {
private readonly cache = new Map<string, HostAgentRequirementResult>()
private readonly generations = new Map<string, object>()
private readonly subscription: Disposable | undefined
constructor(private readonly opts: AgentRequirementsControllerOptions) {
this.subscription = opts.subscribe?.(() => this.clear())
}
clear(): void {
const active = this.cache.size > 0 || this.generations.size > 0
this.cache.clear()
this.generations.clear()
if (!active) return
this.opts.post({ type: "agentRequirementsInvalidated" })
}
dispose(): void {
this.subscription?.dispose()
this.cache.clear()
this.generations.clear()
}
fetch(request: AgentRequirementsRequest): Promise<void> {
const dir = this.scope(request.directory, request.sessionID)
if (!dir) {
this.error(request.agent, request.directory, "scope_mismatch", "The agent requirement scope is no longer active")
return Promise.resolve()
}
return this.load(request.agent, dir, request.force === true).then(
(result) => {
if (!this.scope(dir, request.sessionID)) return
this.opts.post({ type: "agentRequirementsLoaded", result })
},
(error) => {
this.error(request.agent, dir, "request_failed", error)
},
)
}
private scope(requested: string, sessionID?: string): string | undefined {
return requirementDirectory({
requested,
sessionID,
workspaceDirectory: this.opts.root(),
workspaceDirectories: this.opts.folders(),
projectDirectory: this.opts.project(),
sessionDirectories: this.opts.sessions(),
worktreeDirectories: this.opts.worktrees,
})
}
private apply(result: BackendAgentRequirementResult, directory: string): HostAgentRequirementResult {
return applyVSCodeExtensionRequirements({ ...result, directory }, (id) => this.opts.extension(id))
}
private error(agent: string, directory: string, code: "scope_mismatch" | "request_failed", error: unknown): void {
this.opts.post({
type: "agentRequirementsLoaded",
result: {
agent,
directory,
enabled: true,
state: "error",
skills: [],
mcps: [],
vscode_extensions: [],
error: {
code,
message: this.opts.error(error) || "Failed to check agent requirements",
},
},
})
}
async assertAgentRequirements(agent: string | undefined, directory: string): Promise<void> {
if (!agent) return
const dir = this.scope(directory)
if (!dir) throw new Error("The agent requirement scope is no longer active")
const result = await this.load(agent, dir)
if (!this.blocked(result)) return
this.opts.post({ type: "agentRequirementsLoaded", result })
throw new Error(this.message(result))
}
private async load(agent: string, directory: string, force = false): Promise<HostAgentRequirementResult> {
const key = requirementKey(agent, directory)
if (!force) {
const cached = this.cache.get(key)
if (cached) return cached
}
const client = this.opts.client()
if (!client || !this.opts.connected()) throw new Error("Not connected to CLI backend")
const connection = this.opts.generation()
const token = {}
this.generations.set(key, token)
const endpoint = client.kilocode as typeof client.kilocode & AgentRequirementsClient
const response = await endpoint.agentRequirements({ agent, directory }, { throwOnError: true }).catch((error) => {
if (this.generations.get(key) === token) this.generations.delete(key)
throw error
})
if (this.opts.generation() !== connection || this.opts.client() !== client) {
if (this.generations.get(key) === token) this.generations.delete(key)
throw new Error("Connection changed while checking agent requirements")
}
if (this.generations.get(key) !== token) throw new Error("Agent requirement check was superseded")
const result = this.apply(response.data, directory)
this.cache.set(key, result)
this.generations.delete(key)
return result
}
private blocked(result: HostAgentRequirementResult): boolean {
if (!result.enabled || result.state === "disabled") return false
if (result.state === "blocked" || result.state === "error") return true
return (
result.skills.some((item) => item.status !== "ready") ||
result.mcps.some((item) => item.status !== "ready") ||
result.vscode_extensions.some((item) => item.status !== "ready")
)
}
private message(result: HostAgentRequirementResult): string {
if (result.error?.message) return result.error.message
const missing = [
...result.skills.filter((item) => item.status !== "ready").map((item) => `skill ${item.name}`),
...result.mcps.filter((item) => item.status !== "ready").map((item) => `MCP ${item.name}`),
...result.vscode_extensions
.filter((item) => item.status !== "ready")
.map((item) => `VS Code extension ${item.name}`),
]
if (missing.length) return `Agent requirements are not met: ${missing.join(", ")}`
return "Agent requirements are not met"
}
}
@@ -1,103 +0,0 @@
import * as path from "path"
import { sameDirectory } from "../kilo-provider-utils"
export type RequirementStatus = "ready" | "missing" | "error"
export type RequirementState = "disabled" | "ready" | "blocked" | "error"
export type RequirementErrorCode =
| "unknown_agent"
| "malformed_declaration"
| "discovery_failed"
| "mcp_status_failed"
| "scope_mismatch"
| "request_failed"
export type RequirementItem = {
name: string
status: RequirementStatus
message?: string
}
export type BackendVSCodeExtensionRequirement = {
name: string
id: string
message?: string
}
export type BackendAgentRequirementResult = {
agent: string
directory: string
enabled: boolean
state: RequirementState
skills: RequirementItem[]
mcps: RequirementItem[]
vscode_extensions: BackendVSCodeExtensionRequirement[]
error?: {
code: RequirementErrorCode
message: string
}
}
export type HostVSCodeExtensionRequirement = BackendVSCodeExtensionRequirement & {
status: RequirementStatus
}
export type HostAgentRequirementResult = Omit<BackendAgentRequirementResult, "vscode_extensions"> & {
vscode_extensions: HostVSCodeExtensionRequirement[]
}
export type RequirementDirectoryInput = {
requested: string
sessionID?: string
workspaceDirectory: string
workspaceDirectories?: readonly string[]
projectDirectory?: string | null
sessionDirectories: ReadonlyMap<string, string>
worktreeDirectories?: () => readonly string[]
}
export type VSCodeExtensionLookup = (id: string) => unknown
function normalize(directory: string): string {
if (!directory) return ""
const resolved = path.resolve(directory)
return process.platform === "win32" ? resolved.toLowerCase() : resolved
}
export function requirementKey(agent: string, directory: string): string {
return `${normalize(directory)}\0${agent}`
}
export function requirementDirectory(input: RequirementDirectoryInput): string | undefined {
if (!input.requested) return undefined
const dirs = [
input.sessionID ? input.sessionDirectories.get(input.sessionID) : undefined,
input.workspaceDirectory,
...(input.workspaceDirectories ?? []),
input.projectDirectory ?? undefined,
...input.sessionDirectories.values(),
...(input.worktreeDirectories?.() ?? []),
].filter((dir): dir is string => !!dir)
return dirs.find((dir) => sameDirectory(dir, input.requested))
}
export function applyVSCodeExtensionRequirements(
result: BackendAgentRequirementResult,
lookup: VSCodeExtensionLookup,
): HostAgentRequirementResult {
const extensions = result.vscode_extensions.map((extension) => {
const status: RequirementStatus = lookup(extension.id) ? "ready" : "missing"
return { ...extension, status }
})
const blocked =
result.enabled && result.state === "ready" && extensions.some((extension) => extension.status !== "ready")
return {
...result,
state: blocked ? "blocked" : result.state,
vscode_extensions: extensions,
}
}
@@ -41,11 +41,6 @@ export interface AgentContent {
prompt: string
options?: Record<string, unknown>
permission?: Record<string, unknown>
requirements?: {
skills?: string[]
mcps?: string[]
vscode_extensions?: Array<{ name: string; id: string }>
}
}
export interface AgentMarketplaceItem extends MarketplaceItemBase {
@@ -1113,7 +1113,6 @@ describe("Shared webview provider shell", () => {
"ImageModelsProvider",
"NotificationsProvider",
"SessionProvider",
"AgentRequirementsProvider",
"MemoryProvider",
"FeedbackProvider",
])
@@ -1,201 +0,0 @@
import { describe, expect, it } from "bun:test"
import type { KiloClient } from "@kilocode/sdk/v2/client"
import {
applyVSCodeExtensionRequirements,
requirementDirectory,
requirementKey,
type BackendAgentRequirementResult,
type HostAgentRequirementResult,
} from "../../src/kilo-provider/agent-requirements"
import { AgentRequirementsController } from "../../src/kilo-provider/agent-requirements-controller"
import { keepAgentRequirementsResult } from "../../webview-ui/src/context/agent-requirements-state"
const root = "/repo"
function result(input: Partial<BackendAgentRequirementResult> = {}): BackendAgentRequirementResult {
return {
agent: "demo",
directory: root,
enabled: true,
state: "ready",
skills: [],
mcps: [],
vscode_extensions: [],
...input,
}
}
function client(value: BackendAgentRequirementResult | (() => BackendAgentRequirementResult)) {
const calls: Array<{ agent: string; directory: string }> = []
const api = {
kilocode: {
agentRequirements: async (parameters: { agent: string; directory: string }) => {
calls.push(parameters)
return { data: typeof value === "function" ? value() : value }
},
},
} as unknown as KiloClient
return { calls, api }
}
function controller(
input: {
api?: KiloClient | null
posts?: unknown[]
sessions?: ReadonlyMap<string, string>
worktrees?: () => readonly string[]
extensions?: ReadonlySet<string>
subscribe?: (listener: () => void) => { dispose(): void }
} = {},
) {
const posts = input.posts ?? []
return new AgentRequirementsController({
post: (message) => posts.push(message),
client: () => input.api ?? null,
connected: () => true,
generation: () => 1,
root: () => root,
folders: () => [root, "/workspace"],
project: () => root,
sessions: () => input.sessions ?? new Map(),
worktrees: input.worktrees,
extension: (id) => input.extensions?.has(id),
subscribe: input.subscribe,
error: (error) => (error instanceof Error ? error.message : String(error)),
})
}
describe("agent requirement helpers", () => {
it("builds stable cache keys from normalized directories", () => {
expect(requirementKey("demo", "/repo/./subdir")).toBe(requirementKey("demo", "/repo/subdir"))
expect(requirementKey("other", "/repo/subdir")).not.toBe(requirementKey("demo", "/repo/subdir"))
})
it("scopes requests to active workspace, session, and worktree directories", () => {
const sessions = new Map([["session-1", "/repo/.kilo/worktrees/one"]])
const base = {
workspaceDirectory: root,
workspaceDirectories: ["/workspace"],
projectDirectory: root,
sessionDirectories: sessions,
worktreeDirectories: () => ["/repo/.kilo/worktrees/two"],
}
expect(requirementDirectory({ ...base, requested: "/repo" })).toBe(root)
expect(requirementDirectory({ ...base, requested: "/repo/.kilo/worktrees/one", sessionID: "session-1" })).toBe(
"/repo/.kilo/worktrees/one",
)
expect(requirementDirectory({ ...base, requested: "/repo/.kilo/worktrees/two" })).toBe("/repo/.kilo/worktrees/two")
expect(requirementDirectory({ ...base, requested: "/other" })).toBeUndefined()
})
it("adds VS Code extension statuses and blocks ready backend results when extensions are missing", () => {
const checked = applyVSCodeExtensionRequirements(
result({
vscode_extensions: [
{ name: "Ready Extension", id: "publisher.ready" },
{ name: "Missing Extension", id: "publisher.missing" },
],
}),
(id) => id === "publisher.ready",
)
expect(checked.state).toBe("blocked")
expect(checked.vscode_extensions).toEqual([
{ name: "Ready Extension", id: "publisher.ready", status: "ready" },
{ name: "Missing Extension", id: "publisher.missing", status: "missing" },
])
})
})
describe("agent requirement webview state", () => {
const blocked: HostAgentRequirementResult = {
...result({ state: "blocked", skills: [{ name: "skill", status: "missing" }] }),
vscode_extensions: [],
}
it("preserves ready, blocked, and error results only for the same agent and directory", () => {
expect(keepAgentRequirementsResult(blocked, "demo", root)).toBe(true)
expect(keepAgentRequirementsResult({ ...blocked, state: "error" }, "demo", root)).toBe(true)
expect(keepAgentRequirementsResult({ ...blocked, state: "ready" }, "demo", root)).toBe(true)
expect(keepAgentRequirementsResult({ ...blocked, state: "disabled" }, "demo", root)).toBe(false)
expect(keepAgentRequirementsResult(blocked, "other", root)).toBe(false)
expect(keepAgentRequirementsResult(blocked, "demo", "/other")).toBe(false)
})
})
describe("AgentRequirementsController", () => {
it("caches fetched results until invalidated", async () => {
const posts: unknown[] = []
const backend = client(result())
const requirements = controller({ api: backend.api, posts })
await requirements.fetch({ agent: "demo", directory: root })
await requirements.fetch({ agent: "demo", directory: root })
requirements.clear()
await requirements.fetch({ agent: "demo", directory: root })
expect(backend.calls).toEqual([
{ agent: "demo", directory: root },
{ agent: "demo", directory: root },
])
expect(
posts.filter((message) => (message as { type?: string }).type === "agentRequirementsInvalidated"),
).toHaveLength(1)
})
it("posts scope mismatch errors for inactive directories", async () => {
const posts: unknown[] = []
const backend = client(result())
const requirements = controller({ api: backend.api, posts })
await requirements.fetch({ agent: "demo", directory: "/other" })
expect(backend.calls).toEqual([])
expect(posts.at(-1)).toMatchObject({
type: "agentRequirementsLoaded",
result: { state: "error", error: { code: "scope_mismatch" } },
})
})
it("clears requirements when the extension subscription fires", async () => {
const posts: unknown[] = []
let listener: (() => void) | undefined
let disposed = false
const backend = client(result())
const requirements = controller({
api: backend.api,
posts,
subscribe: (fn) => {
listener = fn
return { dispose: () => void (disposed = true) }
},
})
await requirements.fetch({ agent: "demo", directory: root })
posts.length = 0
listener?.()
requirements.dispose()
expect(posts).toEqual([{ type: "agentRequirementsInvalidated" }])
expect(disposed).toBe(true)
})
it("asserts and reports blocked requirements before host sends", async () => {
const posts: unknown[] = []
const backend = client(
result({
vscode_extensions: [{ name: "Missing Extension", id: "publisher.missing" }],
}),
)
const requirements = controller({ api: backend.api, posts })
await expect(requirements.assertAgentRequirements("demo", root)).rejects.toThrow(
"VS Code extension Missing Extension",
)
expect(posts.at(-1)).toMatchObject({
type: "agentRequirementsLoaded",
result: { state: "blocked", vscode_extensions: [{ status: "missing" }] },
})
})
})
@@ -270,17 +270,12 @@ describe("MarketplaceInstaller skills", () => {
})
describe("MarketplaceInstaller agents", () => {
it("preserves requirements in installed agent frontmatter", async () => {
it("preserves agent metadata in installed frontmatter", async () => {
const installer = new MarketplaceInstaller(new TestPaths())
const item = agent({
mode: "all",
description: "Requires local setup",
prompt: "Use the available project tools.",
requirements: {
skills: ["project-skill"],
mcps: ["project-mcp"],
vscode_extensions: [{ name: "Project Helper", id: "publisher.project-helper" }],
},
})
const result = await installer.installAgent(item, "project", tmpDir)
@@ -289,6 +284,6 @@ describe("MarketplaceInstaller agents", () => {
expect(result.filePath).toBeDefined()
if (!result.filePath) throw new Error("agent install did not return a file path")
const data = await frontmatter(result.filePath)
expect(data.requirements).toEqual(item.content.requirements)
expect(data.description).toBe(item.content.description)
})
})
@@ -72,7 +72,6 @@ import { NotificationsProvider } from "../src/context/notifications"
import { FeedbackProvider } from "../src/context/feedback"
import { MemoryProvider } from "../src/context/memory"
import { SessionProvider, useSession } from "../src/context/session"
import { AgentRequirementsProvider } from "../src/context/agent-requirements"
import { WorktreeModeProvider } from "../src/context/worktree-mode"
import { ProviderShell } from "../src/context/provider-shell"
import { ChatView } from "../src/components/chat"
@@ -1,193 +0,0 @@
/** @jsxImportSource solid-js */
import { For, Show, type Component } from "solid-js"
import { Button } from "@kilocode/kilo-ui/button"
import { Card } from "@kilocode/kilo-ui/card"
import { Icon } from "@kilocode/kilo-ui/icon"
import { useAgentRequirements } from "../../context/agent-requirements"
import { useLanguage } from "../../context/language"
import { useVSCode } from "../../context/vscode"
import type { AgentRequirementMCP, AgentRequirementSkill, AgentRequirementVSCodeExtension } from "../../types/messages"
type Status = "ready" | "missing" | "error"
export const AgentRequirements: Component = () => {
const requirements = useAgentRequirements()
const language = useLanguage()
const vscode = useVSCode()
const result = requirements.result
const skills = () => result()?.skills ?? []
const mcps = () => result()?.mcps ?? []
const extensions = () => result()?.vscode_extensions ?? []
const total = () => skills().length + mcps().length + extensions().length
const marketplace = () =>
skills().some((item) => item.status !== "ready") || mcps().some((item) => item.status !== "ready")
const tools = () => {
if (skills().length && mcps().length) {
return `${language.t("agentRequirements.group.skills")} / ${language.t("agentRequirements.group.mcps")}`
}
if (skills().length) return language.t("agentRequirements.group.skills")
return language.t("agentRequirements.group.mcps")
}
const open = () => {
const current = result()
if (!current) return
vscode.postMessage({ type: "openMarketplacePanel", directory: current.directory })
}
const title = () =>
(result()?.agent ?? "")
.split(/[-_]/)
.filter(Boolean)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ")
const error = () => {
const err = result()?.error
if (!err) return undefined
if (err.code === "unknown_agent") return language.t("agentRequirements.error.unknownAgent")
if (err.code === "malformed_declaration") return language.t("agentRequirements.error.malformedDeclaration")
if (err.code === "discovery_failed") return language.t("agentRequirements.error.discoveryFailed")
if (err.code === "mcp_status_failed") return language.t("agentRequirements.error.mcpStatusFailed")
if (err.code === "scope_mismatch") return language.t("agentRequirements.error.scopeMismatch")
return err.message || language.t("agentRequirements.error.requestFailed")
}
const skill = (item: AgentRequirementSkill) => {
if (item.message) return item.message
if (item.status === "ready") return language.t("agentRequirements.skill.installed")
if (item.status === "error") return language.t("agentRequirements.skill.checkFailed")
return language.t("agentRequirements.skill.missing")
}
const mcp = (item: AgentRequirementMCP) => {
if (item.message) return item.message
if (item.status === "ready") return language.t("agentRequirements.mcp.connected")
if (item.status === "error") return language.t("agentRequirements.mcp.checkFailed")
return language.t("agentRequirements.mcp.missing")
}
const extension = (item: AgentRequirementVSCodeExtension) => {
if (item.message) return item.message
if (item.status === "ready") return language.t("agentRequirements.extension.installed")
if (item.status === "error") return language.t("agentRequirements.extension.checkFailed")
return language.t("agentRequirements.extension.missing")
}
const StatusIcon = (props: { status: Status }) => (
<span data-slot="agent-requirements-status" data-status={props.status} aria-hidden="true">
<Show when={props.status === "ready"}>
<Icon name="circle-check" size="small" />
</Show>
<Show when={props.status === "missing"}>
<Icon name="warning" size="small" />
</Show>
<Show when={props.status === "error"}>
<Icon name="circle-x" size="small" />
</Show>
</span>
)
return (
<div class="agent-requirements" role="status" aria-live="polite" aria-atomic="false">
<Card class="agent-requirements-card">
<div data-slot="agent-requirements-copy">
<h2 data-slot="agent-requirements-title">
{language.t("agentRequirements.blocked.title", { agent: title() })}
</h2>
<p data-slot="agent-requirements-description">{language.t("agentRequirements.blocked.description")}</p>
</div>
<Show when={error()} keyed>
{(message) => <div class="agent-requirements-error">{message}</div>}
</Show>
<Show when={total()}>
<div data-slot="agent-requirements-options">
<Show when={skills().length || mcps().length}>
<section class="agent-requirements-category">
<h3 data-slot="agent-requirements-category-title">{tools()}</h3>
<div data-slot="agent-requirements-category-content">
<Show when={skills().length}>
<div class="agent-requirements-group">
<h4 data-slot="agent-requirements-group-title">{language.t("agentRequirements.group.skills")}</h4>
<ul data-slot="agent-requirements-list">
<For each={skills()}>
{(item) => (
<li class="agent-requirements-line" data-status={item.status}>
<StatusIcon status={item.status} />
<div data-slot="agent-requirements-line-row">
<span data-slot="agent-requirements-line-name">{item.name}</span>
<span data-slot="agent-requirements-line-detail">{skill(item)}</span>
</div>
</li>
)}
</For>
</ul>
</div>
</Show>
<Show when={mcps().length}>
<div class="agent-requirements-group">
<h4 data-slot="agent-requirements-group-title">{language.t("agentRequirements.group.mcps")}</h4>
<ul data-slot="agent-requirements-list">
<For each={mcps()}>
{(item) => (
<li class="agent-requirements-line" data-status={item.status}>
<StatusIcon status={item.status} />
<div data-slot="agent-requirements-line-row">
<span data-slot="agent-requirements-line-name">{item.name}</span>
<span data-slot="agent-requirements-line-detail">{mcp(item)}</span>
</div>
</li>
)}
</For>
</ul>
</div>
</Show>
<Show when={marketplace()}>
<div class="agent-requirements-actions">
<Button variant="primary" onClick={open}>
{language.t("agentRequirements.action.openMarketplace")}
</Button>
</div>
</Show>
</div>
</section>
</Show>
<Show when={extensions().length}>
<section class="agent-requirements-category">
<div data-slot="agent-requirements-category-copy">
<h3 data-slot="agent-requirements-category-title">
{language.t("agentRequirements.group.extensions")}
</h3>
<p data-slot="agent-requirements-category-description">
{language.t("agentRequirements.extension.description")}
</p>
</div>
<div data-slot="agent-requirements-category-content">
<div class="agent-requirements-group">
<ul data-slot="agent-requirements-list">
<For each={extensions()}>
{(item) => (
<li class="agent-requirements-line" data-status={item.status}>
<StatusIcon status={item.status} />
<div data-slot="agent-requirements-line-row">
<span data-slot="agent-requirements-line-name">
{item.name} ({item.id})
</span>
<span data-slot="agent-requirements-line-detail">{extension(item)}</span>
</div>
</li>
)}
</For>
</ul>
</div>
</div>
</section>
</Show>
</div>
</Show>
</Card>
</div>
)
}
@@ -14,7 +14,6 @@ import { showToast } from "@kilocode/kilo-ui/toast"
import { DropdownMenu } from "@kilocode/kilo-ui/dropdown-menu"
import { TaskHeader } from "./TaskHeader"
import { MessageList } from "./MessageList"
import { AgentRequirements } from "./AgentRequirements"
import { PromptInput } from "./PromptInput"
import { PermissionDock } from "./PermissionDock"
import { StartupErrorBanner } from "./StartupErrorBanner"
@@ -25,7 +24,6 @@ import { useVSCode } from "../../context/vscode"
import { useLanguage } from "../../context/language"
import { useWorktreeMode } from "../../context/worktree-mode"
import { useServer } from "../../context/server"
import { useAgentRequirements } from "../../context/agent-requirements"
import { TranscriptSearchProvider } from "../../context/transcript-search"
import { isPromptBlocked, isSuggesting, isQuestioning } from "./prompt-input-utils"
import { showTabStrip } from "../../utils/local-tabs"
@@ -53,7 +51,6 @@ export const ChatView: Component<ChatViewProps> = (props) => {
const worktreeMode = useWorktreeMode()
const server = useServer()
const tabs = useLocalTabs()
const requirements = useAgentRequirements()
// Show "Show Changes" only in the standalone sidebar, not inside Agent Manager
const isSidebar = () => worktreeMode === undefined
const pendingSessionID = () => props.pendingSessionID ?? tabs?.pending()
@@ -83,11 +80,9 @@ export const ChatView: Component<ChatViewProps> = (props) => {
const standaloneQuestions = createMemo(() => familyQuestions().filter((q) => !q.tool))
const standaloneSuggestions = createMemo(() => familySuggestions().filter((s) => !s.tool))
const permissionRequest = () => familyPermissions().find((p) => p.sessionID === id()) ?? familyPermissions()[0]
// Questions and suggestions do not block input; permissions and agent requirements do.
// Questions and suggestions do not block input; permissions do.
// Pending questions and suggestions are auto-dismissed in sendMessage/sendCommand.
const blocked = () => isPromptBlocked(familyPermissions().length) || (!props.readonly && requirements.blocked())
const requirementReason = () =>
!props.readonly && requirements.blocked() ? language.t("agentRequirements.prompt.blocked") : undefined
const blocked = () => isPromptBlocked(familyPermissions().length)
// Session is busy only because a suggestion tool call is pending — prompt should behave as idle
const suggesting = () => isSuggesting(blocked(), familySuggestions().length)
// Session is busy only because a question tool call is pending — prompt should behave as idle
@@ -342,24 +337,17 @@ export const ChatView: Component<ChatViewProps> = (props) => {
<TaskHeader readonly={props.readonly} />
<div class="chat-messages-wrapper">
<div class="chat-messages">
<Show
when={!props.readonly && requirements.visible()}
fallback={
<MessageList
onSelectSession={props.onSelectSession}
onShowHistory={props.onShowHistory}
onForkMessage={props.onForkMessage}
questions={standaloneQuestions}
suggestions={standaloneSuggestions}
readonly={props.readonly}
emptyState={props.emptyState}
announce={isSidebar()}
sessionID={pendingSessionID}
/>
}
>
<AgentRequirements />
</Show>
<MessageList
onSelectSession={props.onSelectSession}
onShowHistory={props.onShowHistory}
onForkMessage={props.onForkMessage}
questions={standaloneQuestions}
suggestions={standaloneSuggestions}
readonly={props.readonly}
emptyState={props.emptyState}
announce={isSidebar()}
sessionID={pendingSessionID}
/>
</div>
</div>
@@ -383,7 +371,6 @@ export const ChatView: Component<ChatViewProps> = (props) => {
<Show when={!props.readonly}>
<PromptInput
blocked={blocked}
blockedReason={requirementReason}
suggesting={suggesting}
questioning={questioning}
boxId={props.promptBoxId}
@@ -107,7 +107,6 @@ function beginPending(id: string | undefined) {
interface PromptInputProps {
blocked?: () => boolean
blockedReason?: () => string | undefined
/** When true, session is busy only because a suggestion is pending — treat as idle for input */
suggesting?: () => boolean
/** When true, session is busy only because a question is pending — treat as idle for input */
@@ -536,8 +535,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
!props.blocked?.() &&
(speech.state() === "recording" || (hasInput() && !speech.active()))
const sendLabel = () => {
const reason = props.blockedReason?.()
if (reason) return reason
if (props.blocked?.()) return language.t("prompt.action.send.blocked")
if (speech.state() === "recording") return language.t("prompt.action.send.recording")
return language.t("prompt.action.send")
@@ -1495,19 +1492,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}}
onScroll={syncHighlightScroll}
aria-disabled={isDisabled()}
aria-describedby={props.blockedReason?.() ? blockedHelpId() : undefined}
rows={1}
dir="auto"
/>
</div>
</div>
<Show when={props.blockedReason?.()} keyed>
{(reason) => (
<span id={blockedHelpId()} class="sr-only" role="status">
{reason}
</span>
)}
</Show>
<div class="prompt-input-hint">
<div class="prompt-input-hint-selectors">
<ModeSwitcher sessionID={sid} />
@@ -1613,7 +1602,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
size="small"
onClick={handleSendClick}
aria-disabled={!canSend()}
aria-describedby={props.blockedReason?.() ? blockedHelpId() : undefined}
aria-label={sendLabel()}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
@@ -1,10 +0,0 @@
import type { AgentRequirementResult } from "../types/messages"
export function keepAgentRequirementsResult(
value: AgentRequirementResult | undefined,
agent: string,
directory: string,
) {
if (!value || value.agent !== agent || value.directory !== directory) return false
return value.state === "ready" || value.state === "blocked" || value.state === "error"
}
@@ -1,104 +0,0 @@
/** @jsxImportSource solid-js */
import { createContext, createEffect, createMemo, createSignal, onCleanup, useContext } from "solid-js"
import type { Accessor, ParentComponent } from "solid-js"
import { useConfig } from "./config"
import { useServer } from "./server"
import { useSession } from "./session"
import { useVSCode } from "./vscode"
import type { AgentRequirementResult } from "../types/messages"
import { keepAgentRequirementsResult } from "./agent-requirements-state"
export interface AgentRequirementsContextValue {
result: Accessor<AgentRequirementResult | undefined>
checking: Accessor<boolean>
blocked: Accessor<boolean>
visible: Accessor<boolean>
}
export const AgentRequirementsContext = createContext<AgentRequirementsContextValue>()
export const AgentRequirementsProvider: ParentComponent = (props) => {
const vscode = useVSCode()
const session = useSession()
const server = useServer()
const config = useConfig()
const [result, setResult] = createSignal<AgentRequirementResult>()
const [revision, setRevision] = createSignal(0)
const [pending, setPending] = createSignal(false)
const [requested, setRequested] = createSignal("")
const enabled = () => config.config().experimental?.agent_requirements === true
const agent = () => session.selectedAgent()
const directory = () => server.workspaceDirectory()
const key = () => `${directory()}\0${agent()}`
const active = (value: { agent: string; directory: string }) =>
value.agent === agent() && value.directory === directory()
const request = () => {
if (!enabled() || !agent() || !directory()) return
setPending(true)
vscode.postMessage({
type: "requestAgentRequirements",
agent: agent(),
directory: directory(),
sessionID: session.currentSessionID(),
})
}
const unsubscribe = vscode.onMessage((message) => {
if (message.type === "agentRequirementsLoaded") {
if (!active(message.result)) return
setPending(false)
setResult(message.result)
return
}
if (message.type === "agentRequirementsInvalidated") {
setRequested("")
setRevision((value) => value + 1)
}
})
createEffect(() => {
revision()
if (!enabled()) {
setRequested("")
setPending(false)
setResult(undefined)
return
}
const next = key()
if (!agent() || !directory() || next === requested()) return
setRequested(next)
setPending(true)
if (!keepAgentRequirementsResult(result(), agent(), directory())) setResult(undefined)
request()
})
const checking = createMemo(() => enabled() && pending())
const blocked = createMemo(() => {
if (!enabled()) return false
const current = result()
return !current || current.state === "blocked" || current.state === "error"
})
const visible = createMemo(() => {
const current = result()
return current?.state === "blocked" || current?.state === "error"
})
onCleanup(unsubscribe)
return (
<AgentRequirementsContext.Provider value={{ result, checking, blocked, visible }}>
{props.children}
</AgentRequirementsContext.Provider>
)
}
export function useAgentRequirements(): AgentRequirementsContextValue {
const value = useContext(AgentRequirementsContext)
if (!value) throw new Error("useAgentRequirements must be used within an AgentRequirementsProvider")
return value
}
@@ -15,7 +15,6 @@ import { ProviderProvider } from "./provider"
import { ConfigProvider } from "./config"
import { DisplayProvider } from "./display"
import { IndexingProvider } from "./indexing"
import { AgentRequirementsProvider } from "./agent-requirements"
import { MemoryProvider } from "./memory"
import { SessionProvider } from "./session"
import { LanguageBridge } from "./language-bridge"
@@ -91,11 +90,9 @@ const Session: ParentComponent = (props) => (
)
const Chat: ParentComponent = (props) => (
<AgentRequirementsProvider>
<MemoryProvider>
<FeedbackProvider>{props.children}</FeedbackProvider>
</MemoryProvider>
</AgentRequirementsProvider>
<MemoryProvider>
<FeedbackProvider>{props.children}</FeedbackProvider>
</MemoryProvider>
)
export const ProviderShell = { Root, Session, Chat }
-24
View File
@@ -75,30 +75,6 @@ export const dict = {
"command.session.compact": "ضغط الجلسة",
"command.session.export": "تصدير سجل الجلسة",
"agentRequirements.skill.installed": "مثبت",
"agentRequirements.skill.checkFailed": "فشل فحص المهارة",
"agentRequirements.skill.missing": "غير مثبت",
"agentRequirements.mcp.connected": "متصل",
"agentRequirements.mcp.checkFailed": "فشل فحص MCP",
"agentRequirements.mcp.missing": "غير متصل",
"agentRequirements.extension.installed": "مثبت",
"agentRequirements.extension.checkFailed": "فشل فحص إضافة VS Code",
"agentRequirements.extension.missing": "غير مثبت",
"agentRequirements.extension.description": "ثبّت الإضافات الناقصة في VS Code.",
"agentRequirements.group.skills": "المهارات",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "إضافات VS Code",
"agentRequirements.blocked.title": "متطلبات وكيل {{agent}}",
"agentRequirements.blocked.description": "يحتاج هذا الوكيل إلى الأدوات التالية قبل أن يتمكن من العمل.",
"agentRequirements.prompt.blocked": "أكمل الفحوصات المطلوبة لاستخدام هذا الوكيل أولاً",
"agentRequirements.action.openMarketplace": "فتح Marketplace",
"agentRequirements.error.unknownAgent": "تعذر العثور على الوكيل المحدد.",
"agentRequirements.error.malformedDeclaration": "لدى هذا الوكيل تعريف متطلبات غير صالح.",
"agentRequirements.error.discoveryFailed": "تعذر على Kilo فحص المهارات المتاحة.",
"agentRequirements.error.mcpStatusFailed": "تعذر على Kilo فحص حالة خادم MCP.",
"agentRequirements.error.scopeMismatch": "لم يعد فحص متطلبات هذا الوكيل نشطًا.",
"agentRequirements.error.requestFailed": "تعذر على Kilo فحص متطلبات الوكيل.",
"dialog.provider.search.placeholder": "البحث عن موفرين",
"dialog.provider.empty": "لم يتم العثور على موفرين",
"dialog.provider.group.other": "آخر",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Compactar sessão",
"command.session.export": "Exportar transcrição da sessão",
"agentRequirements.skill.installed": "Instalado",
"agentRequirements.skill.checkFailed": "A verificação da skill falhou",
"agentRequirements.skill.missing": "Não instalado",
"agentRequirements.mcp.connected": "Conectado",
"agentRequirements.mcp.checkFailed": "A verificação do MCP falhou",
"agentRequirements.mcp.missing": "Não conectado",
"agentRequirements.extension.installed": "Instalada",
"agentRequirements.extension.checkFailed": "A verificação da extensão do VS Code falhou",
"agentRequirements.extension.missing": "Não instalada",
"agentRequirements.extension.description": "Instale as extensões ausentes no VS Code.",
"agentRequirements.group.skills": "Skills",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "Extensões do VS Code",
"agentRequirements.blocked.title": "Pré-requisitos do agente {{agent}}",
"agentRequirements.blocked.description": "Este agente precisa das seguintes ferramentas antes de poder executar.",
"agentRequirements.prompt.blocked": "Conclua primeiro as verificações necessárias para usar este agente",
"agentRequirements.action.openMarketplace": "Abrir Marketplace",
"agentRequirements.error.unknownAgent": "O agente selecionado não foi encontrado.",
"agentRequirements.error.malformedDeclaration": "Este agente tem uma declaração de requisitos inválida.",
"agentRequirements.error.discoveryFailed": "O Kilo não conseguiu verificar as skills disponíveis.",
"agentRequirements.error.mcpStatusFailed": "O Kilo não conseguiu verificar o status do servidor MCP.",
"agentRequirements.error.scopeMismatch": "Esta verificação de requisitos do agente não está mais ativa.",
"agentRequirements.error.requestFailed": "O Kilo não conseguiu verificar os requisitos do agente.",
"dialog.provider.search.placeholder": "Buscar provedores",
"dialog.provider.empty": "Nenhum provedor encontrado",
"dialog.provider.group.other": "Outro",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Sažmi sesiju",
"command.session.export": "Izvezi transkript sesije",
"agentRequirements.skill.installed": "Instalirano",
"agentRequirements.skill.checkFailed": "Provjera vještine nije uspjela",
"agentRequirements.skill.missing": "Nije instalirano",
"agentRequirements.mcp.connected": "Povezano",
"agentRequirements.mcp.checkFailed": "Provjera MCP-a nije uspjela",
"agentRequirements.mcp.missing": "Nije povezano",
"agentRequirements.extension.installed": "Instalirano",
"agentRequirements.extension.checkFailed": "Provjera VS Code ekstenzije nije uspjela",
"agentRequirements.extension.missing": "Nije instalirano",
"agentRequirements.extension.description": "Instalirajte nedostajuće ekstenzije u VS Code.",
"agentRequirements.group.skills": "Vještine",
"agentRequirements.group.mcps": "MCP-ovi",
"agentRequirements.group.extensions": "VS Code ekstenzije",
"agentRequirements.blocked.title": "Preduslovi za agenta {{agent}}",
"agentRequirements.blocked.description": "Ovom agentu su potrebni sljedeći alati prije pokretanja.",
"agentRequirements.prompt.blocked": "Prvo dovršite potrebne provjere da biste koristili ovog agenta",
"agentRequirements.action.openMarketplace": "Otvori Marketplace",
"agentRequirements.error.unknownAgent": "Odabrani agent nije pronađen.",
"agentRequirements.error.malformedDeclaration": "Ovaj agent ima neispravnu deklaraciju zahtjeva.",
"agentRequirements.error.discoveryFailed": "Kilo nije mogao provjeriti dostupne vještine.",
"agentRequirements.error.mcpStatusFailed": "Kilo nije mogao provjeriti status MCP servera.",
"agentRequirements.error.scopeMismatch": "Ova provjera zahtjeva agenta više nije aktivna.",
"agentRequirements.error.requestFailed": "Kilo nije mogao provjeriti zahtjeve agenta.",
"dialog.provider.search.placeholder": "Pretraži provajdere",
"dialog.provider.empty": "Nema pronađenih provajdera",
"dialog.provider.group.other": "Ostalo",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Komprimér session",
"command.session.export": "Eksporter sessionsudskrift",
"agentRequirements.skill.installed": "Installeret",
"agentRequirements.skill.checkFailed": "Skill-kontrollen mislykkedes",
"agentRequirements.skill.missing": "Ikke installeret",
"agentRequirements.mcp.connected": "Forbundet",
"agentRequirements.mcp.checkFailed": "MCP-kontrollen mislykkedes",
"agentRequirements.mcp.missing": "Ikke forbundet",
"agentRequirements.extension.installed": "Installeret",
"agentRequirements.extension.checkFailed": "Kontrollen af VS Code-udvidelsen mislykkedes",
"agentRequirements.extension.missing": "Ikke installeret",
"agentRequirements.extension.description": "Installer de manglende udvidelser i VS Code.",
"agentRequirements.group.skills": "Skills",
"agentRequirements.group.mcps": "MCPer",
"agentRequirements.group.extensions": "VS Code-udvidelser",
"agentRequirements.blocked.title": "Forudsætninger for {{agent}}-agenten",
"agentRequirements.blocked.description": "Denne agent skal have følgende værktøjer, før den kan køre.",
"agentRequirements.prompt.blocked": "Fuldfør først de krævede kontroller for at bruge denne agent",
"agentRequirements.action.openMarketplace": "Åbn Marketplace",
"agentRequirements.error.unknownAgent": "Den valgte agent blev ikke fundet.",
"agentRequirements.error.malformedDeclaration": "Denne agent har en ugyldig kravdeklaration.",
"agentRequirements.error.discoveryFailed": "Kilo kunne ikke kontrollere de tilgængelige skills.",
"agentRequirements.error.mcpStatusFailed": "Kilo kunne ikke kontrollere MCP-serverstatus.",
"agentRequirements.error.scopeMismatch": "Denne agentkravskontrol er ikke længere aktiv.",
"agentRequirements.error.requestFailed": "Kilo kunne ikke kontrollere agentkravene.",
"dialog.provider.search.placeholder": "Søg udbydere",
"dialog.provider.empty": "Ingen udbydere fundet",
"dialog.provider.group.other": "Andre",
@@ -84,31 +84,6 @@ export const dict = {
"command.session.compact": "Sitzung komprimieren",
"command.session.export": "Sitzungsprotokoll exportieren",
"agentRequirements.skill.installed": "Installiert",
"agentRequirements.skill.checkFailed": "Die Skill-Prüfung ist fehlgeschlagen",
"agentRequirements.skill.missing": "Nicht installiert",
"agentRequirements.mcp.connected": "Verbunden",
"agentRequirements.mcp.checkFailed": "Die MCP-Prüfung ist fehlgeschlagen",
"agentRequirements.mcp.missing": "Nicht verbunden",
"agentRequirements.extension.installed": "Installiert",
"agentRequirements.extension.checkFailed": "Die Prüfung der VS Code-Erweiterung ist fehlgeschlagen",
"agentRequirements.extension.missing": "Nicht installiert",
"agentRequirements.extension.description": "Installiere die fehlenden Erweiterungen in VS Code.",
"agentRequirements.group.skills": "Skills",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "VS Code-Erweiterungen",
"agentRequirements.blocked.title": "Voraussetzungen für den Agenten {{agent}}",
"agentRequirements.blocked.description":
"Dieser Agent benötigt die folgenden Werkzeuge, bevor er ausgeführt werden kann.",
"agentRequirements.prompt.blocked": "Schließe zuerst die erforderlichen Prüfungen ab, um diesen Agenten zu verwenden",
"agentRequirements.action.openMarketplace": "Marketplace öffnen",
"agentRequirements.error.unknownAgent": "Der ausgewählte Agent wurde nicht gefunden.",
"agentRequirements.error.malformedDeclaration": "Dieser Agent hat eine ungültige Anforderungsdeklaration.",
"agentRequirements.error.discoveryFailed": "Kilo konnte die verfügbaren Skills nicht prüfen.",
"agentRequirements.error.mcpStatusFailed": "Kilo konnte den MCP-Serverstatus nicht prüfen.",
"agentRequirements.error.scopeMismatch": "Diese Prüfung der Agentenanforderungen ist nicht mehr aktiv.",
"agentRequirements.error.requestFailed": "Kilo konnte die Agentenanforderungen nicht prüfen.",
"dialog.provider.search.placeholder": "Anbieter durchsuchen",
"dialog.provider.empty": "Keine Anbieter gefunden",
"dialog.provider.group.other": "Andere",
@@ -77,30 +77,6 @@ export const dict = {
"command.session.compact": "Compact session",
"command.session.export": "Export session transcript",
"agentRequirements.skill.installed": "Installed",
"agentRequirements.skill.checkFailed": "The skill check failed",
"agentRequirements.skill.missing": "Not installed",
"agentRequirements.mcp.connected": "Connected",
"agentRequirements.mcp.checkFailed": "The MCP check failed",
"agentRequirements.mcp.missing": "Not connected",
"agentRequirements.extension.installed": "Installed",
"agentRequirements.extension.checkFailed": "The VS Code extension check failed",
"agentRequirements.extension.missing": "Not installed",
"agentRequirements.extension.description": "Install the missing extensions in VS Code.",
"agentRequirements.group.skills": "Skills",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "VS Code Extensions",
"agentRequirements.blocked.title": "{{agent}} agent prerequisites",
"agentRequirements.blocked.description": "This agent needs the following tools before it can run.",
"agentRequirements.prompt.blocked": "Complete the required checks to use this agent first",
"agentRequirements.action.openMarketplace": "Open Marketplace",
"agentRequirements.error.unknownAgent": "The selected agent could not be found.",
"agentRequirements.error.malformedDeclaration": "This agent has an invalid requirement declaration.",
"agentRequirements.error.discoveryFailed": "Kilo could not check the available skills.",
"agentRequirements.error.mcpStatusFailed": "Kilo could not check MCP server status.",
"agentRequirements.error.scopeMismatch": "This agent requirement check is no longer active.",
"agentRequirements.error.requestFailed": "Kilo could not check the agent requirements.",
"dialog.provider.search.placeholder": "Search providers",
"dialog.provider.empty": "No providers found",
"dialog.provider.group.other": "Other",
-25
View File
@@ -78,31 +78,6 @@ export const dict = {
"command.session.compact": "Compactar sesión",
"command.session.export": "Exportar transcripción de la sesión",
"agentRequirements.skill.installed": "Instalada",
"agentRequirements.skill.checkFailed": "La comprobación de la habilidad falló",
"agentRequirements.skill.missing": "No instalada",
"agentRequirements.mcp.connected": "Conectado",
"agentRequirements.mcp.checkFailed": "La comprobación de MCP falló",
"agentRequirements.mcp.missing": "No conectado",
"agentRequirements.extension.installed": "Instalada",
"agentRequirements.extension.checkFailed": "La comprobación de la extensión de VS Code falló",
"agentRequirements.extension.missing": "No instalada",
"agentRequirements.extension.description": "Instala las extensiones que faltan en VS Code.",
"agentRequirements.group.skills": "Habilidades",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "Extensiones de VS Code",
"agentRequirements.blocked.title": "Requisitos previos del agente {{agent}}",
"agentRequirements.blocked.description":
"Este agente necesita las siguientes herramientas antes de poder ejecutarse.",
"agentRequirements.prompt.blocked": "Completa primero las comprobaciones requeridas para usar este agente",
"agentRequirements.action.openMarketplace": "Abrir Marketplace",
"agentRequirements.error.unknownAgent": "No se pudo encontrar el agente seleccionado.",
"agentRequirements.error.malformedDeclaration": "Este agente tiene una declaración de requisitos no válida.",
"agentRequirements.error.discoveryFailed": "Kilo no pudo comprobar las habilidades disponibles.",
"agentRequirements.error.mcpStatusFailed": "Kilo no pudo comprobar el estado del servidor MCP.",
"agentRequirements.error.scopeMismatch": "Esta comprobación de requisitos del agente ya no está activa.",
"agentRequirements.error.requestFailed": "Kilo no pudo comprobar los requisitos del agente.",
"dialog.provider.search.placeholder": "Buscar proveedores",
"dialog.provider.empty": "No se encontraron proveedores",
"dialog.provider.group.other": "Otro",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "فشرده‌سازی جلسه",
"command.session.export": "صدور رونوشت جلسه",
"agentRequirements.skill.installed": "نصب شده",
"agentRequirements.skill.checkFailed": "بررسی مهارت ناموفق بود",
"agentRequirements.skill.missing": "نصب نشده",
"agentRequirements.mcp.connected": "متصل",
"agentRequirements.mcp.checkFailed": "بررسی MCP ناموفق بود",
"agentRequirements.mcp.missing": "متصل نیست",
"agentRequirements.extension.installed": "نصب شده",
"agentRequirements.extension.checkFailed": "بررسی افزونه VS Code ناموفق بود",
"agentRequirements.extension.missing": "نصب نشده",
"agentRequirements.extension.description": "افزونه‌های مفقود را در VS Code نصب کنید.",
"agentRequirements.group.skills": "مهارت‌ها",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "افزونه‌های VS Code",
"agentRequirements.blocked.title": "پیش‌نیازهای عامل {{agent}}",
"agentRequirements.blocked.description": "این عامل پیش از اجرا به ابزارهای زیر نیاز دارد.",
"agentRequirements.prompt.blocked": "ابتدا بررسی‌های لازم را تکمیل کنید تا بتوانید از این عامل استفاده کنید",
"agentRequirements.action.openMarketplace": "باز کردن Marketplace",
"agentRequirements.error.unknownAgent": "عامل انتخاب‌شده یافت نشد.",
"agentRequirements.error.malformedDeclaration": "این عامل دارای اعلان پیش‌نیاز نامعتبر است.",
"agentRequirements.error.discoveryFailed": "Kilo نتوانست مهارت‌های موجود را بررسی کند.",
"agentRequirements.error.mcpStatusFailed": "Kilo نتوانست وضعیت سرور MCP را بررسی کند.",
"agentRequirements.error.scopeMismatch": "این بررسی نیازمندی‌های عامل دیگر فعال نیست.",
"agentRequirements.error.requestFailed": "Kilo نتوانست نیازمندی‌های عامل را بررسی کند.",
"dialog.provider.search.placeholder": "جستجوی ارائه‌دهندگان",
"dialog.provider.empty": "ارائه‌دهنده‌ای یافت نشد",
"dialog.provider.group.other": "سایر",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Compacter la session",
"command.session.export": "Exporter la transcription de la session",
"agentRequirements.skill.installed": "Installée",
"agentRequirements.skill.checkFailed": "La vérification de la compétence a échoué",
"agentRequirements.skill.missing": "Non installée",
"agentRequirements.mcp.connected": "Connecté",
"agentRequirements.mcp.checkFailed": "La vérification MCP a échoué",
"agentRequirements.mcp.missing": "Non connecté",
"agentRequirements.extension.installed": "Installée",
"agentRequirements.extension.checkFailed": "La vérification de lextension VS Code a échoué",
"agentRequirements.extension.missing": "Non installée",
"agentRequirements.extension.description": "Installez les extensions manquantes dans VS Code.",
"agentRequirements.group.skills": "Compétences",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "Extensions VS Code",
"agentRequirements.blocked.title": "Prérequis de lagent {{agent}}",
"agentRequirements.blocked.description": "Cet agent a besoin des outils suivants avant de pouvoir sexécuter.",
"agentRequirements.prompt.blocked": "Terminez dabord les vérifications requises pour utiliser cet agent",
"agentRequirements.action.openMarketplace": "Ouvrir Marketplace",
"agentRequirements.error.unknownAgent": "Lagent sélectionné est introuvable.",
"agentRequirements.error.malformedDeclaration": "Cet agent a une déclaration de prérequis invalide.",
"agentRequirements.error.discoveryFailed": "Kilo na pas pu vérifier les compétences disponibles.",
"agentRequirements.error.mcpStatusFailed": "Kilo na pas pu vérifier l’état du serveur MCP.",
"agentRequirements.error.scopeMismatch": "Cette vérification des prérequis de lagent nest plus active.",
"agentRequirements.error.requestFailed": "Kilo na pas pu vérifier les prérequis de lagent.",
"dialog.provider.search.placeholder": "Rechercher des fournisseurs",
"dialog.provider.empty": "Aucun fournisseur trouvé",
"dialog.provider.group.other": "Autre",
-25
View File
@@ -1194,31 +1194,6 @@ export const dict = {
// Session export
"command.session.export": "Esporta trascrizione sessione",
"agentRequirements.skill.installed": "Installata",
"agentRequirements.skill.checkFailed": "Il controllo della skill non è riuscito",
"agentRequirements.skill.missing": "Non installata",
"agentRequirements.mcp.connected": "Connesso",
"agentRequirements.mcp.checkFailed": "Il controllo MCP non è riuscito",
"agentRequirements.mcp.missing": "Non connesso",
"agentRequirements.extension.installed": "Installata",
"agentRequirements.extension.checkFailed": "Il controllo dellestensione VS Code non è riuscito",
"agentRequirements.extension.missing": "Non installata",
"agentRequirements.extension.description": "Installa le estensioni mancanti in VS Code.",
"agentRequirements.group.skills": "Skill",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "Estensioni VS Code",
"agentRequirements.blocked.title": "Prerequisiti dellagente {{agent}}",
"agentRequirements.blocked.description":
"Questo agente richiede i seguenti strumenti prima di poter essere eseguito.",
"agentRequirements.prompt.blocked": "Completa prima i controlli richiesti per usare questo agente",
"agentRequirements.action.openMarketplace": "Apri Marketplace",
"agentRequirements.error.unknownAgent": "Impossibile trovare lagente selezionato.",
"agentRequirements.error.malformedDeclaration": "Questo agente ha una dichiarazione dei requisiti non valida.",
"agentRequirements.error.discoveryFailed": "Kilo non ha potuto controllare le skill disponibili.",
"agentRequirements.error.mcpStatusFailed": "Kilo non ha potuto controllare lo stato del server MCP.",
"agentRequirements.error.scopeMismatch": "Questo controllo dei requisiti dellagente non è più attivo.",
"agentRequirements.error.requestFailed": "Kilo non ha potuto controllare i requisiti dellagente.",
// Model groups
// Plan exit
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "セッションを圧縮",
"command.session.export": "セッション記録をエクスポート",
"agentRequirements.skill.installed": "インストール済み",
"agentRequirements.skill.checkFailed": "スキルの確認に失敗しました",
"agentRequirements.skill.missing": "未インストール",
"agentRequirements.mcp.connected": "接続済み",
"agentRequirements.mcp.checkFailed": "MCP の確認に失敗しました",
"agentRequirements.mcp.missing": "未接続",
"agentRequirements.extension.installed": "インストール済み",
"agentRequirements.extension.checkFailed": "VS Code 拡張機能の確認に失敗しました",
"agentRequirements.extension.missing": "未インストール",
"agentRequirements.extension.description": "不足している拡張機能を VS Code にインストールしてください。",
"agentRequirements.group.skills": "スキル",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "VS Code 拡張機能",
"agentRequirements.blocked.title": "{{agent}} エージェントの前提条件",
"agentRequirements.blocked.description": "このエージェントを実行するには、次のツールが必要です。",
"agentRequirements.prompt.blocked": "このエージェントを使用するには、先に必要な確認を完了してください",
"agentRequirements.action.openMarketplace": "Marketplace を開く",
"agentRequirements.error.unknownAgent": "選択したエージェントが見つかりませんでした。",
"agentRequirements.error.malformedDeclaration": "このエージェントの要件宣言は無効です。",
"agentRequirements.error.discoveryFailed": "Kilo は利用可能なスキルを確認できませんでした。",
"agentRequirements.error.mcpStatusFailed": "Kilo は MCP サーバーの状態を確認できませんでした。",
"agentRequirements.error.scopeMismatch": "このエージェント要件の確認はもう有効ではありません。",
"agentRequirements.error.requestFailed": "Kilo はエージェント要件を確認できませんでした。",
"dialog.provider.search.placeholder": "プロバイダーを検索",
"dialog.provider.empty": "プロバイダーが見つかりません",
"dialog.provider.group.other": "その他",
-24
View File
@@ -82,30 +82,6 @@ export const dict = {
"command.session.compact": "세션 압축",
"command.session.export": "세션 기록 내보내기",
"agentRequirements.skill.installed": "설치됨",
"agentRequirements.skill.checkFailed": "스킬 확인에 실패했습니다",
"agentRequirements.skill.missing": "설치되지 않음",
"agentRequirements.mcp.connected": "연결됨",
"agentRequirements.mcp.checkFailed": "MCP 확인에 실패했습니다",
"agentRequirements.mcp.missing": "연결되지 않음",
"agentRequirements.extension.installed": "설치됨",
"agentRequirements.extension.checkFailed": "VS Code 확장 확인에 실패했습니다",
"agentRequirements.extension.missing": "설치되지 않음",
"agentRequirements.extension.description": "VS Code에서 누락된 확장을 설치하세요.",
"agentRequirements.group.skills": "스킬",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "VS Code 확장",
"agentRequirements.blocked.title": "{{agent}} 에이전트 필수 조건",
"agentRequirements.blocked.description": "이 에이전트를 실행하려면 다음 도구가 필요합니다.",
"agentRequirements.prompt.blocked": "이 에이전트를 사용하려면 먼저 필요한 확인을 완료하세요",
"agentRequirements.action.openMarketplace": "Marketplace 열기",
"agentRequirements.error.unknownAgent": "선택한 에이전트를 찾을 수 없습니다.",
"agentRequirements.error.malformedDeclaration": "이 에이전트의 요구 사항 선언이 올바르지 않습니다.",
"agentRequirements.error.discoveryFailed": "Kilo가 사용 가능한 스킬을 확인하지 못했습니다.",
"agentRequirements.error.mcpStatusFailed": "Kilo가 MCP 서버 상태를 확인하지 못했습니다.",
"agentRequirements.error.scopeMismatch": "이 에이전트 요구 사항 확인은 더 이상 활성 상태가 아닙니다.",
"agentRequirements.error.requestFailed": "Kilo가 에이전트 요구 사항을 확인하지 못했습니다.",
"dialog.provider.search.placeholder": "공급자 검색",
"dialog.provider.empty": "공급자 없음",
"dialog.provider.group.other": "기타",
-25
View File
@@ -78,31 +78,6 @@ export const dict = {
"command.session.compact": "Sessie comprimeren",
"command.session.export": "Sessietranscript exporteren",
"agentRequirements.skill.installed": "Geïnstalleerd",
"agentRequirements.skill.checkFailed": "De skillcontrole is mislukt",
"agentRequirements.skill.missing": "Niet geïnstalleerd",
"agentRequirements.mcp.connected": "Verbonden",
"agentRequirements.mcp.checkFailed": "De MCP-controle is mislukt",
"agentRequirements.mcp.missing": "Niet verbonden",
"agentRequirements.extension.installed": "Geïnstalleerd",
"agentRequirements.extension.checkFailed": "De controle van de VS Code-extensie is mislukt",
"agentRequirements.extension.missing": "Niet geïnstalleerd",
"agentRequirements.extension.description": "Installeer de ontbrekende extensies in VS Code.",
"agentRequirements.group.skills": "Skills",
"agentRequirements.group.mcps": "MCPs",
"agentRequirements.group.extensions": "VS Code-extensies",
"agentRequirements.blocked.title": "Vereisten voor agent {{agent}}",
"agentRequirements.blocked.description":
"Deze agent heeft de volgende tools nodig voordat deze kan worden uitgevoerd.",
"agentRequirements.prompt.blocked": "Voltooi eerst de vereiste controles om deze agent te gebruiken",
"agentRequirements.action.openMarketplace": "Marketplace openen",
"agentRequirements.error.unknownAgent": "De geselecteerde agent kon niet worden gevonden.",
"agentRequirements.error.malformedDeclaration": "Deze agent heeft een ongeldige vereistenverklaring.",
"agentRequirements.error.discoveryFailed": "Kilo kon de beschikbare skills niet controleren.",
"agentRequirements.error.mcpStatusFailed": "Kilo kon de MCP-serverstatus niet controleren.",
"agentRequirements.error.scopeMismatch": "Deze controle van agentvereisten is niet meer actief.",
"agentRequirements.error.requestFailed": "Kilo kon de agentvereisten niet controleren.",
"dialog.provider.search.placeholder": "Providers zoeken",
"dialog.provider.empty": "Geen providers gevonden",
"dialog.provider.group.other": "Overige",
-24
View File
@@ -81,30 +81,6 @@ export const dict = {
"command.session.compact": "Komprimer sesjon",
"command.session.export": "Eksporter sesjonsutskrift",
"agentRequirements.skill.installed": "Installert",
"agentRequirements.skill.checkFailed": "Skill-sjekken mislyktes",
"agentRequirements.skill.missing": "Ikke installert",
"agentRequirements.mcp.connected": "Tilkoblet",
"agentRequirements.mcp.checkFailed": "MCP-sjekken mislyktes",
"agentRequirements.mcp.missing": "Ikke tilkoblet",
"agentRequirements.extension.installed": "Installert",
"agentRequirements.extension.checkFailed": "Sjekken av VS Code-utvidelsen mislyktes",
"agentRequirements.extension.missing": "Ikke installert",
"agentRequirements.extension.description": "Installer de manglende utvidelsene i VS Code.",
"agentRequirements.group.skills": "Skills",
"agentRequirements.group.mcps": "MCP-er",
"agentRequirements.group.extensions": "VS Code-utvidelser",
"agentRequirements.blocked.title": "Forutsetninger for agenten {{agent}}",
"agentRequirements.blocked.description": "Denne agenten trenger følgende verktøy før den kan kjøre.",
"agentRequirements.prompt.blocked": "Fullfør først de nødvendige sjekkene for å bruke denne agenten",
"agentRequirements.action.openMarketplace": "Åpne Marketplace",
"agentRequirements.error.unknownAgent": "Den valgte agenten ble ikke funnet.",
"agentRequirements.error.malformedDeclaration": "Denne agenten har en ugyldig kravdeklarasjon.",
"agentRequirements.error.discoveryFailed": "Kilo kunne ikke sjekke tilgjengelige skills.",
"agentRequirements.error.mcpStatusFailed": "Kilo kunne ikke sjekke MCP-serverstatus.",
"agentRequirements.error.scopeMismatch": "Denne sjekken av agentkrav er ikke lenger aktiv.",
"agentRequirements.error.requestFailed": "Kilo kunne ikke sjekke agentkravene.",
"dialog.provider.search.placeholder": "Søk etter leverandører",
"dialog.provider.empty": "Ingen leverandører funnet",
"dialog.provider.group.other": "Andre",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Kompaktuj sesję",
"command.session.export": "Eksportuj transkrypcję sesji",
"agentRequirements.skill.installed": "Zainstalowano",
"agentRequirements.skill.checkFailed": "Sprawdzanie umiejętności nie powiodło się",
"agentRequirements.skill.missing": "Nie zainstalowano",
"agentRequirements.mcp.connected": "Połączono",
"agentRequirements.mcp.checkFailed": "Sprawdzanie MCP nie powiodło się",
"agentRequirements.mcp.missing": "Nie połączono",
"agentRequirements.extension.installed": "Zainstalowano",
"agentRequirements.extension.checkFailed": "Sprawdzanie rozszerzenia VS Code nie powiodło się",
"agentRequirements.extension.missing": "Nie zainstalowano",
"agentRequirements.extension.description": "Zainstaluj brakujące rozszerzenia w VS Code.",
"agentRequirements.group.skills": "Umiejętności",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "Rozszerzenia VS Code",
"agentRequirements.blocked.title": "Wymagania wstępne agenta {{agent}}",
"agentRequirements.blocked.description": "Ten agent potrzebuje następujących narzędzi, zanim będzie mógł działać.",
"agentRequirements.prompt.blocked": "Najpierw ukończ wymagane sprawdzenia, aby użyć tego agenta",
"agentRequirements.action.openMarketplace": "Otwórz Marketplace",
"agentRequirements.error.unknownAgent": "Nie znaleziono wybranego agenta.",
"agentRequirements.error.malformedDeclaration": "Ten agent ma nieprawidłową deklarację wymagań.",
"agentRequirements.error.discoveryFailed": "Kilo nie mógł sprawdzić dostępnych umiejętności.",
"agentRequirements.error.mcpStatusFailed": "Kilo nie mógł sprawdzić stanu serwera MCP.",
"agentRequirements.error.scopeMismatch": "To sprawdzenie wymagań agenta nie jest już aktywne.",
"agentRequirements.error.requestFailed": "Kilo nie mógł sprawdzić wymagań agenta.",
"dialog.provider.search.placeholder": "Szukaj dostawców",
"dialog.provider.empty": "Nie znaleziono dostawców",
"dialog.provider.group.other": "Inne",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Сжать сессию",
"command.session.export": "Экспортировать запись сеанса",
"agentRequirements.skill.installed": "Установлено",
"agentRequirements.skill.checkFailed": "Не удалось проверить навык",
"agentRequirements.skill.missing": "Не установлено",
"agentRequirements.mcp.connected": "Подключено",
"agentRequirements.mcp.checkFailed": "Не удалось проверить MCP",
"agentRequirements.mcp.missing": "Не подключено",
"agentRequirements.extension.installed": "Установлено",
"agentRequirements.extension.checkFailed": "Не удалось проверить расширение VS Code",
"agentRequirements.extension.missing": "Не установлено",
"agentRequirements.extension.description": "Установите отсутствующие расширения в VS Code.",
"agentRequirements.group.skills": "Навыки",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "Расширения VS Code",
"agentRequirements.blocked.title": "Требования агента {{agent}}",
"agentRequirements.blocked.description": "Этому агенту нужны следующие инструменты перед запуском.",
"agentRequirements.prompt.blocked": "Сначала выполните необходимые проверки, чтобы использовать этого агента",
"agentRequirements.action.openMarketplace": "Открыть Marketplace",
"agentRequirements.error.unknownAgent": "Выбранный агент не найден.",
"agentRequirements.error.malformedDeclaration": "У этого агента недопустимое объявление требований.",
"agentRequirements.error.discoveryFailed": "Kilo не смог проверить доступные навыки.",
"agentRequirements.error.mcpStatusFailed": "Kilo не смог проверить состояние сервера MCP.",
"agentRequirements.error.scopeMismatch": "Эта проверка требований агента больше не активна.",
"agentRequirements.error.requestFailed": "Kilo не смог проверить требования агента.",
"dialog.provider.search.placeholder": "Поиск провайдеров",
"dialog.provider.empty": "Провайдеры не найдены",
"dialog.provider.group.other": "Другие",
-24
View File
@@ -77,30 +77,6 @@ export const dict = {
"command.session.compact": "บีบอัดเซสชัน",
"command.session.export": "ส่งออกบันทึกเซสชัน",
"agentRequirements.skill.installed": "ติดตั้งแล้ว",
"agentRequirements.skill.checkFailed": "การตรวจสอบสกิลล้มเหลว",
"agentRequirements.skill.missing": "ยังไม่ได้ติดตั้ง",
"agentRequirements.mcp.connected": "เชื่อมต่อแล้ว",
"agentRequirements.mcp.checkFailed": "การตรวจสอบ MCP ล้มเหลว",
"agentRequirements.mcp.missing": "ยังไม่ได้เชื่อมต่อ",
"agentRequirements.extension.installed": "ติดตั้งแล้ว",
"agentRequirements.extension.checkFailed": "การตรวจสอบส่วนขยาย VS Code ล้มเหลว",
"agentRequirements.extension.missing": "ยังไม่ได้ติดตั้ง",
"agentRequirements.extension.description": "ติดตั้งส่วนขยายที่ขาดหายใน VS Code",
"agentRequirements.group.skills": "สกิล",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "ส่วนขยาย VS Code",
"agentRequirements.blocked.title": "ข้อกำหนดเบื้องต้นของเอเจนต์ {{agent}}",
"agentRequirements.blocked.description": "เอเจนต์นี้ต้องใช้เครื่องมือต่อไปนี้ก่อนจึงจะทำงานได้",
"agentRequirements.prompt.blocked": "ทำการตรวจสอบที่จำเป็นให้เสร็จก่อนเพื่อใช้เอเจนต์นี้",
"agentRequirements.action.openMarketplace": "เปิด Marketplace",
"agentRequirements.error.unknownAgent": "ไม่พบเอเจนต์ที่เลือก",
"agentRequirements.error.malformedDeclaration": "เอเจนต์นี้มีการประกาศข้อกำหนดที่ไม่ถูกต้อง",
"agentRequirements.error.discoveryFailed": "Kilo ไม่สามารถตรวจสอบสกิลที่มีอยู่ได้",
"agentRequirements.error.mcpStatusFailed": "Kilo ไม่สามารถตรวจสอบสถานะเซิร์ฟเวอร์ MCP ได้",
"agentRequirements.error.scopeMismatch": "การตรวจสอบข้อกำหนดของเอเจนต์นี้ไม่ทำงานอีกต่อไป",
"agentRequirements.error.requestFailed": "Kilo ไม่สามารถตรวจสอบข้อกำหนดของเอเจนต์ได้",
"dialog.provider.search.placeholder": "ค้นหาผู้ให้บริการ",
"dialog.provider.empty": "ไม่พบผู้ให้บริการ",
"dialog.provider.group.other": "อื่น ๆ",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Oturumu sıkıştır",
"command.session.export": "Oturum dökümünü dışa aktar",
"agentRequirements.skill.installed": "Yüklü",
"agentRequirements.skill.checkFailed": "Yetenek denetimi başarısız oldu",
"agentRequirements.skill.missing": "Yüklü değil",
"agentRequirements.mcp.connected": "Bağlı",
"agentRequirements.mcp.checkFailed": "MCP denetimi başarısız oldu",
"agentRequirements.mcp.missing": "Bağlı değil",
"agentRequirements.extension.installed": "Yüklü",
"agentRequirements.extension.checkFailed": "VS Code uzantısı denetimi başarısız oldu",
"agentRequirements.extension.missing": "Yüklü değil",
"agentRequirements.extension.description": "Eksik uzantıları VS Code içinde yükleyin.",
"agentRequirements.group.skills": "Yetenekler",
"agentRequirements.group.mcps": "MCPler",
"agentRequirements.group.extensions": "VS Code Uzantıları",
"agentRequirements.blocked.title": "{{agent}} ajanı ön koşulları",
"agentRequirements.blocked.description": "Bu ajanın çalışmadan önce aşağıdaki araçlara ihtiyacı var.",
"agentRequirements.prompt.blocked": "Bu ajanı kullanmak için önce gerekli denetimleri tamamlayın",
"agentRequirements.action.openMarketplace": "Marketplacei Aç",
"agentRequirements.error.unknownAgent": "Seçili ajan bulunamadı.",
"agentRequirements.error.malformedDeclaration": "Bu ajanın geçersiz bir gereksinim bildirimi var.",
"agentRequirements.error.discoveryFailed": "Kilo mevcut yetenekleri denetleyemedi.",
"agentRequirements.error.mcpStatusFailed": "Kilo MCP sunucu durumunu denetleyemedi.",
"agentRequirements.error.scopeMismatch": "Bu ajan gereksinimi denetimi artık etkin değil.",
"agentRequirements.error.requestFailed": "Kilo ajan gereksinimlerini denetleyemedi.",
"dialog.provider.search.placeholder": "Sağlayıcı ara",
"dialog.provider.empty": "Sağlayıcı bulunamadı",
"dialog.provider.group.other": "Diğer",
-24
View File
@@ -78,30 +78,6 @@ export const dict = {
"command.session.compact": "Стиснути сесію",
"command.session.export": "Експортувати запис сеансу",
"agentRequirements.skill.installed": "Встановлено",
"agentRequirements.skill.checkFailed": "Не вдалося перевірити навичку",
"agentRequirements.skill.missing": "Не встановлено",
"agentRequirements.mcp.connected": "Підключено",
"agentRequirements.mcp.checkFailed": "Не вдалося перевірити MCP",
"agentRequirements.mcp.missing": "Не підключено",
"agentRequirements.extension.installed": "Встановлено",
"agentRequirements.extension.checkFailed": "Не вдалося перевірити розширення VS Code",
"agentRequirements.extension.missing": "Не встановлено",
"agentRequirements.extension.description": "Встановіть відсутні розширення у VS Code.",
"agentRequirements.group.skills": "Навички",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "Розширення VS Code",
"agentRequirements.blocked.title": "Передумови агента {{agent}}",
"agentRequirements.blocked.description": "Цьому агенту потрібні такі інструменти, перш ніж він зможе працювати.",
"agentRequirements.prompt.blocked": "Спочатку завершіть необхідні перевірки, щоб використати цього агента",
"agentRequirements.action.openMarketplace": "Відкрити Marketplace",
"agentRequirements.error.unknownAgent": "Вибраного агента не знайдено.",
"agentRequirements.error.malformedDeclaration": "Цей агент має недійсну декларацію вимог.",
"agentRequirements.error.discoveryFailed": "Kilo не зміг перевірити доступні навички.",
"agentRequirements.error.mcpStatusFailed": "Kilo не зміг перевірити стан сервера MCP.",
"agentRequirements.error.scopeMismatch": "Ця перевірка вимог агента більше не активна.",
"agentRequirements.error.requestFailed": "Kilo не зміг перевірити вимоги агента.",
"dialog.provider.search.placeholder": "Пошук провайдера",
"dialog.provider.empty": "Провайдерів не знайдено",
"dialog.provider.group.other": "Інші",
-24
View File
@@ -76,30 +76,6 @@ export const dict = {
"command.session.compact": "精简会话",
"command.session.export": "导出会话记录",
"agentRequirements.skill.installed": "已安装",
"agentRequirements.skill.checkFailed": "技能检查失败",
"agentRequirements.skill.missing": "未安装",
"agentRequirements.mcp.connected": "已连接",
"agentRequirements.mcp.checkFailed": "MCP 检查失败",
"agentRequirements.mcp.missing": "未连接",
"agentRequirements.extension.installed": "已安装",
"agentRequirements.extension.checkFailed": "VS Code 扩展检查失败",
"agentRequirements.extension.missing": "未安装",
"agentRequirements.extension.description": "在 VS Code 中安装缺少的扩展。",
"agentRequirements.group.skills": "技能",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "VS Code 扩展",
"agentRequirements.blocked.title": "{{agent}} 代理前置要求",
"agentRequirements.blocked.description": "此代理需要以下工具后才能运行。",
"agentRequirements.prompt.blocked": "请先完成所需检查,然后再使用此代理",
"agentRequirements.action.openMarketplace": "打开 Marketplace",
"agentRequirements.error.unknownAgent": "找不到所选代理。",
"agentRequirements.error.malformedDeclaration": "此代理的要求声明无效。",
"agentRequirements.error.discoveryFailed": "Kilo 无法检查可用技能。",
"agentRequirements.error.mcpStatusFailed": "Kilo 无法检查 MCP 服务器状态。",
"agentRequirements.error.scopeMismatch": "此代理要求检查已不再有效。",
"agentRequirements.error.requestFailed": "Kilo 无法检查代理要求。",
"dialog.provider.search.placeholder": "搜索提供商",
"dialog.provider.empty": "未找到提供商",
"dialog.provider.group.other": "其他",
-24
View File
@@ -76,30 +76,6 @@ export const dict = {
"command.session.compact": "精簡工作階段",
"command.session.export": "匯出會話紀錄",
"agentRequirements.skill.installed": "已安裝",
"agentRequirements.skill.checkFailed": "技能檢查失敗",
"agentRequirements.skill.missing": "未安裝",
"agentRequirements.mcp.connected": "已連線",
"agentRequirements.mcp.checkFailed": "MCP 檢查失敗",
"agentRequirements.mcp.missing": "未連線",
"agentRequirements.extension.installed": "已安裝",
"agentRequirements.extension.checkFailed": "VS Code 擴充功能檢查失敗",
"agentRequirements.extension.missing": "未安裝",
"agentRequirements.extension.description": "在 VS Code 中安裝缺少的擴充功能。",
"agentRequirements.group.skills": "技能",
"agentRequirements.group.mcps": "MCP",
"agentRequirements.group.extensions": "VS Code 擴充功能",
"agentRequirements.blocked.title": "{{agent}} 代理前置需求",
"agentRequirements.blocked.description": "此代理需要以下工具後才能執行。",
"agentRequirements.prompt.blocked": "請先完成必要檢查,才能使用此代理",
"agentRequirements.action.openMarketplace": "開啟 Marketplace",
"agentRequirements.error.unknownAgent": "找不到所選代理。",
"agentRequirements.error.malformedDeclaration": "此代理的需求宣告無效。",
"agentRequirements.error.discoveryFailed": "Kilo 無法檢查可用技能。",
"agentRequirements.error.mcpStatusFailed": "Kilo 無法檢查 MCP 伺服器狀態。",
"agentRequirements.error.scopeMismatch": "此代理需求檢查已不再有效。",
"agentRequirements.error.requestFailed": "Kilo 無法檢查代理需求。",
"dialog.provider.search.placeholder": "搜尋供應商",
"dialog.provider.empty": "找不到供應商",
"dialog.provider.group.other": "其他",
@@ -30,7 +30,6 @@ import { Diff } from "@kilocode/kilo-ui/diff"
import { Code } from "@kilocode/kilo-ui/code"
import { File } from "@kilocode/kilo-ui/file"
import { SessionContext } from "../context/session"
import { AgentRequirementsContext, type AgentRequirementsContextValue } from "../context/agent-requirements"
import { NotificationsContext } from "../context/notifications"
import { LanguageContext } from "../context/language"
import { IndexingProvider } from "../context/indexing"
@@ -52,7 +51,6 @@ import type {
SessionCloseReason,
QuestionRequest,
SuggestionRequest,
AgentRequirementResult,
} from "../types/messages"
type PluginSpec = string | [string, Record<string, unknown>]
@@ -299,9 +297,6 @@ interface StoryProvidersProps {
questions?: QuestionRequest[]
suggestions?: SuggestionRequest[]
notifications?: KilocodeNotification[]
agentRequirements?: AgentRequirementResult
agentRequirementsChecking?: boolean
agentRequirementsBlocked?: boolean
status?: string
sessionID?: string
/** When provided, injects a mock ConfigContext with this config instead of the real ConfigProvider. */
@@ -416,23 +411,6 @@ export const StoryProviders: ParentComponent<StoryProvidersProps> = (props) => {
})
const notifications = mockNotificationsValue(props.notifications)
const [locale] = createSignal<"en">("en")
const result = () => props.agentRequirements
const visible = () => {
const value = result()
return value?.state === "blocked" || value?.state === "error"
}
const requirements: AgentRequirementsContextValue = {
result,
checking: () => props.agentRequirementsChecking ?? false,
blocked: () => {
if (props.agentRequirementsBlocked !== undefined) return props.agentRequirementsBlocked
const value = result()
if (!value) return props.agentRequirementsChecking === true
return value.enabled && (value.state === "blocked" || value.state === "error")
},
visible,
}
return (
<VSCodeProvider>
<ServerProvider>
@@ -460,36 +438,34 @@ export const StoryProviders: ParentComponent<StoryProvidersProps> = (props) => {
<I18nProvider value={{ locale: () => "en", t, plural }}>
<NotificationsContext.Provider value={notifications}>
<SessionContext.Provider value={session as any}>
<AgentRequirementsContext.Provider value={requirements}>
<MemoryProvider>
<IndexingProvider>
<KiloEmbeddingModelsProvider>
<DataProvider
data={data()}
directory="/project/"
onOpenDiff={props.onOpenDiff}
onOpenFile={props.onOpenFile}
>
<DiffComponentProvider component={Diff}>
<CodeComponentProvider component={Code}>
<FileComponentProvider component={File}>
<MarkedProvider>
<TranscriptSearchProvider>
{props.noPadding ? (
props.children
) : (
<div style={{ padding: "12px" }}>{props.children}</div>
)}
</TranscriptSearchProvider>
</MarkedProvider>
</FileComponentProvider>
</CodeComponentProvider>
</DiffComponentProvider>
</DataProvider>
</KiloEmbeddingModelsProvider>
</IndexingProvider>
</MemoryProvider>
</AgentRequirementsContext.Provider>
<MemoryProvider>
<IndexingProvider>
<KiloEmbeddingModelsProvider>
<DataProvider
data={data()}
directory="/project/"
onOpenDiff={props.onOpenDiff}
onOpenFile={props.onOpenFile}
>
<DiffComponentProvider component={Diff}>
<CodeComponentProvider component={Code}>
<FileComponentProvider component={File}>
<MarkedProvider>
<TranscriptSearchProvider>
{props.noPadding ? (
props.children
) : (
<div style={{ padding: "12px" }}>{props.children}</div>
)}
</TranscriptSearchProvider>
</MarkedProvider>
</FileComponentProvider>
</CodeComponentProvider>
</DiffComponentProvider>
</DataProvider>
</KiloEmbeddingModelsProvider>
</IndexingProvider>
</MemoryProvider>
</SessionContext.Provider>
</NotificationsContext.Provider>
</I18nProvider>
@@ -26,7 +26,6 @@ import { ProviderContext } from "../context/provider"
import { ServerContext } from "../context/server"
import { WorktreeModeProvider } from "../context/worktree-mode"
import type {
AgentRequirementResult,
Message,
Part,
QuestionRequest,
@@ -40,68 +39,6 @@ import { reviewMetadata } from "../../../src/shared/review-comments"
const SESSION_ID = "story-session-chat-001"
const missingToolsRequirements: AgentRequirementResult = {
agent: "code-review",
directory: "/project",
enabled: true,
state: "blocked",
skills: [
{ name: "review-checklist", status: "ready" },
{ name: "security-audit", status: "missing" },
],
mcps: [
{ name: "github", status: "missing" },
{ name: "filesystem", status: "ready" },
],
vscode_extensions: [],
}
const missingExtensionRequirements: AgentRequirementResult = {
agent: "release-review",
directory: "/project",
enabled: true,
state: "blocked",
skills: [],
mcps: [],
vscode_extensions: [
{
name: "GitHub Pull Requests",
id: "github.vscode-pull-request-github",
status: "missing",
},
],
}
const malformedRequirements: AgentRequirementResult = {
agent: "malformed-agent",
directory: "/project",
enabled: true,
state: "error",
skills: [],
mcps: [],
vscode_extensions: [],
error: {
code: "malformed_declaration",
message: "Invalid requirements declaration.",
},
}
const readyRequirements: AgentRequirementResult = {
agent: "ready-agent",
directory: "/project",
enabled: true,
state: "ready",
skills: [{ name: "review-checklist", status: "ready" }],
mcps: [{ name: "filesystem", status: "ready" }],
vscode_extensions: [
{
name: "GitHub Pull Requests",
id: "github.vscode-pull-request-github",
status: "ready",
},
],
}
// ---------------------------------------------------------------------------
// Question fixtures
// ---------------------------------------------------------------------------
@@ -228,71 +165,6 @@ export const ChatViewWithMessages: Story = {
},
}
export const ChatViewRequirementsChecking: Story = {
name: "ChatView — agent requirements checking",
render: () => (
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding agentRequirementsChecking agentRequirementsBlocked>
<ServerContext.Provider value={mockServer as any}>
<div style={{ height: "600px", display: "flex", "flex-direction": "column" }}>
<ChatView />
</div>
</ServerContext.Provider>
</StoryProviders>
),
}
export const ChatViewRequirementsMissingTools: Story = {
name: "ChatView — missing skills and MCPs",
render: () => (
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding agentRequirements={missingToolsRequirements}>
<ServerContext.Provider value={mockServer as any}>
<div style={{ height: "600px", display: "flex", "flex-direction": "column" }}>
<ChatView />
</div>
</ServerContext.Provider>
</StoryProviders>
),
}
export const ChatViewRequirementsMissingExtension: Story = {
name: "ChatView — missing VS Code extension",
render: () => (
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding agentRequirements={missingExtensionRequirements}>
<ServerContext.Provider value={mockServer as any}>
<div style={{ height: "600px", display: "flex", "flex-direction": "column" }}>
<ChatView />
</div>
</ServerContext.Provider>
</StoryProviders>
),
}
export const ChatViewRequirementsMalformed: Story = {
name: "ChatView — malformed agent requirements",
render: () => (
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding agentRequirements={malformedRequirements}>
<ServerContext.Provider value={mockServer as any}>
<div style={{ height: "600px", display: "flex", "flex-direction": "column" }}>
<ChatView />
</div>
</ServerContext.Provider>
</StoryProviders>
),
}
export const ChatViewRequirementsReady: Story = {
name: "ChatView — requirements ready (no card)",
render: () => (
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding agentRequirements={readyRequirements}>
<ServerContext.Provider value={mockServer as any}>
<div style={{ height: "600px", display: "flex", "flex-direction": "column" }}>
<ChatView />
</div>
</ServerContext.Provider>
</StoryProviders>
),
}
export const ChatViewAgentManagerCompleted: Story = {
name: "ChatView — completed Agent Manager session actions",
render: () => {
@@ -1,294 +0,0 @@
/* ============================================
Agent Requirements
============================================ */
.agent-requirements {
height: 100%;
overflow: auto;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 16px var(--chat-gutter, 16px);
animation: agent-requirements-enter 360ms ease-out 100ms both;
}
[data-component="card"].agent-requirements-card {
width: 100%;
max-width: 430px;
display: flex;
flex-direction: column;
gap: 20px;
padding: 18px;
border: 1px solid var(--border-weak-base, var(--vscode-panel-border));
border-radius: 10px;
background: var(--vscode-editorWidget-background);
color: var(--vscode-foreground);
text-align: left;
box-shadow: 0 12px 32px rgb(0 0 0 / 22%);
}
@keyframes agent-requirements-enter {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.agent-requirements {
animation: none;
}
}
[data-slot="agent-requirements-copy"] {
display: flex;
flex-direction: column;
gap: 8px;
}
[data-slot="agent-requirements-title-row"] {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
[data-slot="agent-requirements-title"] {
margin: 0;
color: var(--vscode-foreground);
font-size: var(--kilo-font-size-14);
font-weight: 650;
line-height: 1.35;
}
[data-slot="agent-requirements-title-row"] > [data-component="icon"] {
--icon-base: var(--icon-success-base);
flex: 0 0 auto;
}
[data-slot="agent-requirements-description"] {
margin: 0;
color: var(--text-weak-base, var(--vscode-descriptionForeground));
font-size: var(--kilo-font-size-12);
line-height: 1.45;
}
.agent-requirements-error {
margin: 0;
padding: 10px;
border: 1px solid var(--border-critical-base);
border-radius: 8px;
background: color-mix(in srgb, var(--surface-critical-base) 34%, var(--vscode-editor-background));
color: var(--text-on-critical-base);
font-size: var(--kilo-font-size-12);
line-height: 1.45;
}
[data-slot="agent-requirements-options"] {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
min-width: 0;
}
.agent-requirements-category {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 0;
}
[data-slot="agent-requirements-category-copy"] {
display: flex;
flex-direction: column;
gap: 4px;
}
[data-slot="agent-requirements-category-title"] {
margin: 0;
color: var(--vscode-textLink-foreground);
font-size: var(--kilo-font-size-11);
font-weight: 650;
line-height: 1.25;
letter-spacing: 0.04em;
text-transform: uppercase;
}
[data-slot="agent-requirements-category-description"] {
margin: 0;
color: var(--text-weak-base, var(--vscode-descriptionForeground));
font-size: var(--kilo-font-size-12);
line-height: 1.4;
}
[data-slot="agent-requirements-category-content"] {
display: flex;
flex-direction: column;
gap: 14px;
min-width: 0;
}
.agent-requirements-group {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
color: var(--vscode-foreground);
}
[data-slot="agent-requirements-group-title"] {
margin: 0;
color: var(--vscode-foreground);
font-size: var(--kilo-font-size-12);
font-weight: 650;
line-height: 1.35;
}
[data-slot="agent-requirements-list"] {
display: flex;
flex-direction: column;
gap: 6px;
margin: 0;
padding: 0;
color: var(--vscode-foreground);
list-style: none;
font-size: var(--kilo-font-size-12);
line-height: 1.4;
}
.agent-requirements-line {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
padding: 7px 0;
border-bottom: 1px solid color-mix(in srgb, var(--border-weak-base, var(--vscode-panel-border)) 60%, transparent);
}
.agent-requirements-line:last-child {
border-bottom: 0;
}
[data-slot="agent-requirements-status"] {
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
border-radius: 6px;
}
[data-slot="agent-requirements-status"][data-status="ready"] {
color: var(--vscode-charts-green, #89d185);
background: color-mix(in srgb, var(--vscode-charts-green, #89d185) 12%, transparent);
}
[data-slot="agent-requirements-status"][data-status="missing"] {
color: var(--vscode-charts-yellow, #e2c08d);
background: color-mix(in srgb, var(--vscode-charts-yellow, #e2c08d) 13%, transparent);
}
[data-slot="agent-requirements-status"][data-status="error"] {
color: var(--icon-critical-base);
background: color-mix(in srgb, var(--surface-critical-base) 50%, transparent);
}
[data-slot="agent-requirements-status"] [data-component="icon"],
[data-slot="agent-requirements-status"] [data-component="spinner"] {
width: 14px;
height: 14px;
}
[data-slot="agent-requirements-line-row"] {
display: flex;
align-items: baseline;
justify-content: space-between;
flex: 1;
gap: 12px;
min-width: 0;
}
[data-slot="agent-requirements-line-name"] {
min-width: 0;
overflow-wrap: anywhere;
color: var(--vscode-foreground);
}
[data-slot="agent-requirements-line-detail"] {
flex: 0 0 auto;
max-width: 48%;
overflow-wrap: anywhere;
color: var(--text-weak-base, var(--vscode-descriptionForeground));
font-size: var(--kilo-font-size-11);
line-height: 1.35;
text-align: right;
}
.agent-requirements-line[data-status="ready"] [data-slot="agent-requirements-line-detail"] {
color: var(--vscode-charts-green, #89d185);
font-weight: 600;
}
.agent-requirements-line[data-status="missing"] [data-slot="agent-requirements-line-detail"] {
color: var(--vscode-charts-yellow, #e2c08d);
}
.agent-requirements-line[data-status="error"] [data-slot="agent-requirements-line-detail"] {
color: var(--text-on-critical-base, var(--vscode-errorForeground));
}
[data-slot="agent-requirements-extension-note"] {
margin: 0;
color: var(--text-weak-base, var(--vscode-descriptionForeground));
font-size: var(--kilo-font-size-11);
line-height: 1.4;
}
.agent-requirements-actions {
display: flex;
justify-content: flex-end;
flex-wrap: wrap;
gap: 8px;
}
@container chat (max-width: 320px) {
.agent-requirements {
align-items: flex-start;
padding: 12px 8px;
}
[data-component="card"].agent-requirements-card {
gap: 16px;
padding: 12px;
}
[data-slot="agent-requirements-options"] {
gap: 12px;
}
.agent-requirements-line {
align-items: flex-start;
}
[data-slot="agent-requirements-line-row"] {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
[data-slot="agent-requirements-line-detail"] {
max-width: 100%;
text-align: left;
}
.agent-requirements-actions {
flex-direction: column;
}
}
@@ -15,7 +15,6 @@
@import "./session-actions.css";
@import "./welcome.css";
@import "./prompt-input.css";
@import "./agent-requirements.css";
@import "./prompt-dropdowns.css";
@import "./model-selector.css";
@import "./permission-dock.css";
@@ -41,11 +41,6 @@ export interface AgentContent {
prompt: string
options?: Record<string, unknown>
permission?: Record<string, unknown>
requirements?: {
skills?: string[]
mcps?: string[]
vscode_extensions?: Array<{ name: string; id: string }>
}
}
export interface AgentMarketplaceItem extends MarketplaceItemBase {
@@ -32,51 +32,6 @@ export interface AgentInfo {
permission?: PermissionRuleItem[]
}
export interface AgentRequirementSkill {
name: string
status: "ready" | "missing" | "error"
message?: string
}
export interface AgentRequirementMCP {
name: string
status: "ready" | "missing" | "error"
message?: string
}
export interface AgentRequirementVSCodeExtension {
name: string
id: string
status: "ready" | "missing" | "error"
message?: string
}
export interface AgentRequirementResult {
agent: string
directory: string
enabled: boolean
state: "disabled" | "ready" | "blocked" | "error"
skills: AgentRequirementSkill[]
mcps: AgentRequirementMCP[]
vscode_extensions: AgentRequirementVSCodeExtension[]
error?: {
code:
| "unknown_agent"
| "malformed_declaration"
| "discovery_failed"
| "mcp_status_failed"
| "scope_mismatch"
| "request_failed"
message: string
}
}
export interface AgentRequirements {
skills?: string[]
mcps?: string[]
vscode_extensions?: Array<{ name: string; id: string }>
}
export interface AgentConfig {
model?: string | null
variant?: string | null
@@ -88,6 +43,5 @@ export interface AgentConfig {
temperature?: number | null
top_p?: number | null
steps?: number | null
requirements?: AgentRequirements
permission?: PermissionConfig
}
@@ -52,7 +52,6 @@ export interface ExperimentalConfig {
batch_tool?: boolean
image_generation?: boolean
image_generation_model?: string
agent_requirements?: boolean
native_notebook_tools?: boolean
speech_to_text_model?: string
primary_tools?: string[]
@@ -20,7 +20,7 @@ import type { AnacondaDesktopExtensionMessage } from "../../../../src/shared/ana
import type { QuestionRequest, SuggestionRequest, TodoItem } from "./questions"
import type { ModelSelection, ModelUsageMap, Provider, ProviderAuthState } from "./providers"
import type { SpeechToTextModelDef } from "../../../../src/speech-to-text/models"
import type { AgentInfo, AgentRequirementResult, SkillInfo, SlashCommandInfo } from "./agents"
import type { AgentInfo, SkillInfo, SlashCommandInfo } from "./agents"
import type {
BrowserSettings,
Config,
@@ -412,15 +412,6 @@ export interface SkillsLoadedMessage {
skills: SkillInfo[]
}
export interface AgentRequirementsLoadedMessage {
type: "agentRequirementsLoaded"
result: AgentRequirementResult
}
export interface AgentRequirementsInvalidatedMessage {
type: "agentRequirementsInvalidated"
}
export interface CommandsLoadedMessage {
type: "commandsLoaded"
commands: SlashCommandInfo[]
@@ -1335,8 +1326,6 @@ export type ExtensionMessage =
| ProvidersLoadedMessage
| AgentsLoadedMessage
| SkillsLoadedMessage
| AgentRequirementsLoadedMessage
| AgentRequirementsInvalidatedMessage
| CommandsLoadedMessage
| AutocompleteSettingsLoadedMessage
| ChatCompletionResultMessage
@@ -273,14 +273,6 @@ export interface RequestSkillsMessage {
type: "requestSkills"
}
export interface RequestAgentRequirementsMessage {
type: "requestAgentRequirements"
agent: string
directory: string
sessionID?: string
force?: boolean
}
export interface RequestCommandsMessage {
type: "requestCommands"
}
@@ -1434,7 +1426,6 @@ export type WebviewMessage =
| CompactRequest
| RequestAgentsMessage
| RequestSkillsMessage
| RequestAgentRequirementsMessage
| RequestCommandsMessage
| SendCommandRequest
| RemoveSkillMessage
+2 -42
View File
@@ -30,9 +30,7 @@ import { AbsolutePath, type DeepMutable } from "@opencode-ai/core/schema"
// kilocode_change start
import * as KiloAgent from "@/kilocode/agent"
import { RuntimeFlags } from "@/effect/runtime-flags"
import * as AgentRequirements from "@/kilocode/agent-requirements"
import * as KiloReference from "@/kilocode/reference"
import { MCP } from "@/mcp"
// kilocode_change end
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
@@ -41,8 +39,6 @@ import { Reference } from "@opencode-ai/core/reference"
import { Location } from "@opencode-ai/core/location"
import { PluginV2 } from "@opencode-ai/core/plugin"
export type RequirementBlockedError = InstanceType<typeof AgentRequirements.BlockedError> // kilocode_change
export const Info = Schema.Struct({
name: Schema.String,
// kilocode_change start
@@ -67,7 +63,6 @@ export const Info = Schema.Struct({
variant: Schema.optional(Schema.String),
prompt: Schema.optional(Schema.String),
options: Schema.Record(Schema.String, Schema.Unknown),
requirements: Schema.optional(AgentRequirements.Requirements), // kilocode_change
steps: Schema.optional(Schema.Finite),
}).annotate({ identifier: "Agent" })
export type Info = DeepMutable<Schema.Schema.Type<typeof Info>>
@@ -83,10 +78,6 @@ export interface Interface {
readonly list: () => Effect.Effect<Info[]>
readonly defaultInfo: () => Effect.Effect<Info>
readonly defaultAgent: () => Effect.Effect<string>
// kilocode_change start
readonly requirementStatus: (agent: string) => Effect.Effect<AgentRequirements.Result>
readonly guardRequirements: (agent: Info) => Effect.Effect<void, RequirementBlockedError>
// kilocode_change end
readonly generate: (input: {
description: string
model?: { providerID: ProviderV2.ID; modelID: ModelV2.ID }
@@ -100,7 +91,7 @@ export interface Interface {
>
}
type State = Omit<Interface, "generate" | "requirementStatus" | "guardRequirements"> & { version: string } // kilocode_change
type State = Omit<Interface, "generate"> & { version: string } // kilocode_change
export class Service extends Context.Service<Service, Interface>()("@opencode/Agent") {}
@@ -113,7 +104,6 @@ const layer = Layer.effect(
const auth = yield* Auth.Service
const plugin = yield* Plugin.Service
const skill = yield* Skill.Service
const mcp = yield* MCP.Service // kilocode_change
const provider = yield* Provider.Service
const flags = yield* RuntimeFlags.Service // kilocode_change
const locations = yield* LocationServiceMap.Service
@@ -376,7 +366,6 @@ const layer = Layer.effect(
// kilocode_change start - carry metadata as typed fields, never as provider options
item.displayName = value.displayName ?? item.displayName
item.source = value.source ?? item.source
item.requirements = value.requirements ?? item.requirements
// kilocode_change end
item.options = mergeDeep(item.options, value.options ?? {})
item.permission = Permission.merge(item.permission, Permission.fromConfig(value.permission ?? {}))
@@ -536,31 +525,6 @@ const layer = Layer.effect(
return yield* select(yield* InstanceState.get(state))
})
const requirementStatus = Effect.fn("Agent.requirementStatus")(function* (name: string) {
const ctx = yield* InstanceState.context
return yield* AgentRequirements.status({
name,
directory: ctx.directory,
config,
skills: skill,
mcp,
agents: { get: (agent) => current((s) => s.get(agent)) },
})
})
const guardRequirements = Effect.fn("Agent.guardRequirements")(function* (agent: Info) {
const ctx = yield* InstanceState.context
yield* AgentRequirements.guard({
agent,
directory: ctx.directory,
config,
skills: skill,
mcp,
agents: { get: (name) => current((s) => s.get(name)) },
})
})
// kilocode_change end
return Service.of({
get: Effect.fn("Agent.get")(function* (agent: string) {
return yield* current((s) => s.get(agent)) // kilocode_change
@@ -574,10 +538,6 @@ const layer = Layer.effect(
defaultAgent: Effect.fn("Agent.defaultAgent")(function* () {
return yield* current((s) => s.defaultAgent()) // kilocode_change
}),
// kilocode_change start
requirementStatus,
guardRequirements,
// kilocode_change end
generate: Effect.fn("Agent.generate")(function* (input: {
description: string
model?: { providerID: ProviderV2.ID; modelID: ModelV2.ID }
@@ -653,7 +613,7 @@ const locationServiceMapNode = LayerNode.make({
export const node = LayerNode.make({
service: Service,
layer: layer,
deps: [Config.node, Auth.node, Plugin.node, Skill.node, Provider.node, MCP.node, RuntimeFlags.node, locationServiceMapNode], // kilocode_change
deps: [Config.node, Auth.node, Plugin.node, Skill.node, Provider.node, RuntimeFlags.node, locationServiceMapNode], // kilocode_change
})
export * as Agent from "./agent"
@@ -1,261 +0,0 @@
import type { Agent } from "@/agent/agent"
import type { Config } from "@/config/config"
import type { MCP } from "@/mcp"
import type { Skill } from "@/skill"
import { Flag } from "@opencode-ai/core/flag/flag"
import { NamedError } from "@opencode-ai/core/util/error"
import { Cause, Effect, Exit, Schema } from "effect"
import { ConfigAgentV1 } from "@opencode-ai/core/v1/config/agent"
export const VSCodeExtension = ConfigAgentV1.VSCodeExtension
export type VSCodeExtension = ConfigAgentV1.VSCodeExtension
export const Requirements = ConfigAgentV1.Requirements
export type Requirements = ConfigAgentV1.Requirements
export const SkillItem = Schema.Struct({
name: Schema.String,
status: Schema.Literals(["ready", "missing", "error"]),
message: Schema.optional(Schema.String),
})
export type SkillItem = Schema.Schema.Type<typeof SkillItem>
export const MCPItem = Schema.Struct({
name: Schema.String,
status: Schema.Literals(["ready", "missing", "error"]),
message: Schema.optional(Schema.String),
})
export type MCPItem = Schema.Schema.Type<typeof MCPItem>
export const Result = Schema.Struct({
agent: Schema.String,
directory: Schema.String,
enabled: Schema.Boolean,
state: Schema.Literals(["disabled", "ready", "blocked", "error"]),
skills: Schema.Array(SkillItem),
mcps: Schema.Array(MCPItem),
vscode_extensions: Schema.Array(VSCodeExtension),
error: Schema.optional(
Schema.Struct({
code: Schema.Literals(["unknown_agent", "malformed_declaration", "discovery_failed", "mcp_status_failed"]),
message: Schema.String,
}),
),
}).annotate({ identifier: "AgentRequirementResult" })
export type Result = Schema.Schema.Type<typeof Result>
export const BlockedError = NamedError.create("AgentRequirementError", {
message: Schema.String,
agent: Schema.String,
directory: Schema.String,
state: Schema.Literals(["blocked", "error"]),
skills: Schema.mutable(Schema.Array(SkillItem)),
mcps: Schema.mutable(Schema.Array(MCPItem)),
vscode_extensions: Schema.mutable(Schema.Array(VSCodeExtension)),
})
type AgentInfo = Pick<Agent.Info, "name"> & { requirements?: unknown }
type Services = {
config: Pick<Config.Interface, "get">
agents: { get: (agent: string) => Effect.Effect<AgentInfo | undefined> }
skills: Pick<Skill.Interface, "all">
mcp: Pick<MCP.Interface, "status">
}
function enabled(cfg: Config.Info) {
const experimental = cfg.experimental
return experimental !== undefined && "agent_requirements" in experimental && experimental.agent_requirements === true
}
function ready(input: { agent: string; directory: string; enabled: boolean }): Result {
return {
...input,
state: input.enabled ? "ready" : "disabled",
skills: [],
mcps: [],
vscode_extensions: [],
}
}
function malformed(agent: AgentInfo, directory: string, message: string): Result {
return {
agent: agent.name,
directory,
enabled: true,
state: "error",
skills: [],
mcps: [],
vscode_extensions: [],
error: { code: "malformed_declaration", message },
}
}
function decode(agent: AgentInfo, directory: string): Requirements | Result {
if (agent.requirements === undefined) return ready({ agent: agent.name, directory, enabled: true })
const decoded = Schema.decodeUnknownExit(Requirements)(agent.requirements, {
errors: "all",
propertyOrder: "original",
})
if (Exit.isFailure(decoded)) return malformed(agent, directory, Cause.pretty(decoded.cause))
return decoded.value
}
function item(name: string, status: MCP.Status | undefined): MCPItem {
if (status?.status === "connected") return { name, status: "ready" }
if (status?.status === "failed" || status?.status === "needs_client_registration") {
return { name, status: "error", message: status.error }
}
return { name, status: "missing" }
}
export function evaluate(input: {
agent: AgentInfo
directory: string
enabled: boolean
requirements: Requirements
discovered?: ReadonlySet<string>
discoveryError?: string
mcp?: Readonly<Record<string, MCP.Status>>
mcpError?: string
}): Result {
if (!input.enabled) return ready({ agent: input.agent.name, directory: input.directory, enabled: false })
const skills = (input.requirements.skills ?? []).map((skill) => ({
name: skill,
status: input.discoveryError
? ("error" as const)
: input.discovered?.has(skill)
? ("ready" as const)
: ("missing" as const),
...(input.discoveryError ? { message: input.discoveryError } : {}),
}))
const mcps = (input.requirements.mcps ?? []).map((name) => {
if (input.mcpError) return { name, status: "error" as const, message: input.mcpError }
return item(name, input.mcp?.[name])
})
if (input.discoveryError) {
return {
agent: input.agent.name,
directory: input.directory,
enabled: true,
state: "error",
skills,
mcps,
vscode_extensions: input.requirements.vscode_extensions ?? [],
error: { code: "discovery_failed", message: input.discoveryError },
}
}
if (input.mcpError) {
return {
agent: input.agent.name,
directory: input.directory,
enabled: true,
state: "error",
skills,
mcps,
vscode_extensions: input.requirements.vscode_extensions ?? [],
error: { code: "mcp_status_failed", message: input.mcpError },
}
}
const valid = skills.every((skill) => skill.status === "ready") && mcps.every((mcp) => mcp.status === "ready")
return {
agent: input.agent.name,
directory: input.directory,
enabled: true,
state: valid ? "ready" : "blocked",
skills,
mcps,
vscode_extensions: input.requirements.vscode_extensions ?? [],
}
}
export const status = Effect.fn("AgentRequirements.status")(function* (
input: Services & { name: string; directory: string },
) {
const cfg = yield* input.config.get()
const active = enabled(cfg)
if (!active) return ready({ agent: input.name, directory: input.directory, enabled: false })
const agent = yield* input.agents.get(input.name)
if (!agent) {
return {
agent: input.name,
directory: input.directory,
enabled: true,
state: "error",
skills: [],
mcps: [],
vscode_extensions: [],
error: { code: "unknown_agent", message: `Agent not found: ${input.name}` },
} satisfies Result
}
const requirements = decode(agent, input.directory)
if ("state" in requirements) return requirements
const skillStatus: Effect.Effect<Skill.Info[]> = requirements.skills?.length ? input.skills.all() : Effect.succeed([])
const mcpStatus: Effect.Effect<Record<string, MCP.Status>> = requirements.mcps?.length
? input.mcp.status()
: Effect.succeed({})
const [discovered, mcp] = yield* Effect.all([skillStatus.pipe(Effect.exit), mcpStatus.pipe(Effect.exit)])
const discoveredSet: ReadonlySet<string> | undefined = Exit.isSuccess(discovered)
? new Set(discovered.value.map((skill) => skill.name))
: undefined
if (Exit.isFailure(discovered)) {
return evaluate({
agent,
directory: input.directory,
enabled: active,
requirements,
discoveryError: Cause.pretty(discovered.cause),
mcp: Exit.isSuccess(mcp) ? mcp.value : undefined,
})
}
if (Exit.isFailure(mcp)) {
return evaluate({
agent,
directory: input.directory,
enabled: active,
requirements,
discovered: discoveredSet,
mcpError: Cause.pretty(mcp.cause),
})
}
return evaluate({
agent,
directory: input.directory,
enabled: active,
requirements,
discovered: discoveredSet,
mcp: mcp.value,
})
})
export const guard = Effect.fn("AgentRequirements.guard")(function* (
input: Services & { agent: AgentInfo; directory: string },
) {
const result = yield* status({ ...input, name: input.agent.name })
const unsupported = Flag.KILO_CLIENT !== "vscode" && result.vscode_extensions.length > 0
if (result.state === "disabled" || (result.state === "ready" && !unsupported)) return
const state = result.state === "error" ? result.state : "blocked"
return yield* Effect.fail(
new BlockedError({
message: "Complete the required checks to use this agent first",
agent: result.agent,
directory: result.directory,
state,
skills: [...result.skills],
mcps: [...result.mcps],
vscode_extensions: [...result.vscode_extensions],
}),
)
})
@@ -1,116 +0,0 @@
import type { AgentRequirementResult } from "@kilocode/sdk/v2"
import { isRecord } from "@/util/record"
type Item = AgentRequirementResult["skills"][number] | AgentRequirementResult["mcps"][number]
export type Client = {
kilocode: {
agentRequirements: (
parameters: { agent: string; directory: string },
options: { throwOnError: true },
) => Promise<{ data: AgentRequirementResult }>
}
}
export type ErrorData = {
message: string
agent: string
directory: string
state: "blocked" | "error"
skills: AgentRequirementResult["skills"]
mcps: AgentRequirementResult["mcps"]
vscode_extensions: AgentRequirementResult["vscode_extensions"]
}
export type RequirementError = {
name: "AgentRequirementError"
data: ErrorData
}
export type Check =
| { ok: true; result: AgentRequirementResult }
| { ok: false; result: AgentRequirementResult; error: RequirementError }
export async function check(input: { client: Client; agent: string; directory: string }): Promise<Check> {
const response = await input.client.kilocode.agentRequirements(
{ agent: input.agent, directory: input.directory },
{ throwOnError: true },
)
if (!blocked(response.data)) return { ok: true, result: response.data }
return { ok: false, result: response.data, error: toError(response.data) }
}
export function blocked(result: AgentRequirementResult) {
if (!result.enabled || result.state === "disabled") return false
if (result.vscode_extensions.length > 0) return true
return result.state === "blocked" || result.state === "error"
}
export function toError(result: AgentRequirementResult): RequirementError {
const info = {
message: result.error?.message ?? "Complete the required checks to use this agent first",
agent: result.agent,
directory: result.directory,
state: result.state === "error" ? ("error" as const) : ("blocked" as const),
skills: result.skills,
mcps: result.mcps,
vscode_extensions: result.vscode_extensions,
}
return {
name: "AgentRequirementError",
data: {
...info,
message: format(info),
},
}
}
export function data(input: unknown): ErrorData | undefined {
if (!isRecord(input)) return undefined
if (input.name !== "AgentRequirementError" || !isRecord(input.data)) return undefined
const info = input.data
if (typeof info.message !== "string") return undefined
if (typeof info.agent !== "string") return undefined
if (typeof info.directory !== "string") return undefined
if (info.state !== "blocked" && info.state !== "error") return undefined
if (!Array.isArray(info.skills)) return undefined
if (!Array.isArray(info.mcps)) return undefined
if (!Array.isArray(info.vscode_extensions)) return undefined
return info as ErrorData
}
export function format(input: ErrorData | RequirementError) {
const info = "data" in input ? input.data : input
const skills = pending(info.skills)
const mcps = pending(info.mcps)
const lines = [`Agent requirements are not met for "${info.agent}".`]
if (info.vscode_extensions.length) {
lines.push(
"",
"VS Code extensions:",
...info.vscode_extensions.map((ext) => `- ${ext.name} (${ext.id})`),
"This agent requires VS Code extensions and is not supported in this CLI environment. Use the Kilo VS Code extension instead.",
)
}
if (skills.length) lines.push("", "Skills:", ...skills.map((skill) => `- ${item(skill)}`))
if (mcps.length) lines.push("", "MCP servers:", ...mcps.map((mcp) => `- ${item(mcp)}`))
if (skills.length || mcps.length) {
lines.push("", "Install the required skills and configure or connect the required MCP servers, then retry.")
}
return lines.join("\n")
}
function pending<T extends Item>(items: T[]) {
return items.filter((item) => item.status !== "ready")
}
function item(input: Item) {
const status = input.status === "missing" ? "missing" : "error"
return input.message ? `${input.name} (${status}: ${input.message})` : `${input.name} (${status})`
}
@@ -5,11 +5,9 @@ import { InstanceContextMiddleware } from "@/server/routes/instance/httpapi/midd
import {
WorkspaceRoutingMiddleware,
WorkspaceRoutingQuery,
WorkspaceRoutingQueryFields,
} from "@/server/routes/instance/httpapi/middleware/workspace-routing"
import { described } from "@/server/routes/instance/httpapi/groups/metadata"
import { AnacondaDesktopApi } from "./anaconda-desktop"
import { Result as AgentRequirementResult } from "@/kilocode/agent-requirements"
import {
Failure as AgentManagerFailure,
Request as AgentManagerRequest,
@@ -42,10 +40,6 @@ export const RemoveAgentPayload = Schema.Struct({
scope: Schema.optional(Scope),
})
export const AgentRequirementQuery = Schema.Struct({
...WorkspaceRoutingQueryFields,
agent: Schema.String,
})
export const NotebookReplyPayload = Schema.Struct({ result: NotebookResult })
export const NotebookRejectPayload = Schema.Struct({ error: NotebookFailure })
export const AgentManagerReplyPayload = Schema.Struct({ result: AgentManagerResult })
@@ -53,7 +47,6 @@ export const AgentManagerRejectPayload = Schema.Struct({ error: AgentManagerFail
export const KilocodePaths = {
heapSnapshot: `${root}/heap/snapshot`,
agentRequirements: `${root}/agent/requirements`,
commandFiles: `${root}/command/files`,
removeCommand: `${root}/command/remove`,
removeSkill: `${root}/skill/remove`,
@@ -82,16 +75,6 @@ export const KilocodeApi = HttpApi.make("kilocode")
description: "Write a heap snapshot for the CLI process to the log directory.",
}),
),
HttpApiEndpoint.get("agentRequirements", KilocodePaths.agentRequirements, {
query: AgentRequirementQuery,
success: described(AgentRequirementResult, "Agent requirement status"),
}).annotateMerge(
OpenApi.annotations({
identifier: "kilocode.agentRequirements",
summary: "Check agent requirements",
description: "Check whether the selected agent's requirements are available in the request directory.",
}),
),
HttpApiEndpoint.get("commandFiles", KilocodePaths.commandFiles, {
query: WorkspaceRoutingQuery,
success: described(Schema.Array(CommandFiles.Info), "Command files"),
@@ -42,12 +42,6 @@ export const kilocodeHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilocode"
return yield* Effect.sync(() => HeapSnapshot.write())
})
const agentRequirements = Effect.fn("KilocodeHttpApi.agentRequirements")(function* (ctx: {
query: { agent: string }
}) {
return yield* agents.requirementStatus(ctx.query.agent)
})
const commandFiles = Effect.fn("KilocodeHttpApi.commandFiles")(function* () {
const instance = yield* InstanceState.context
const dirs = yield* config.directories()
@@ -177,7 +171,6 @@ export const kilocodeHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilocode"
return handlers
.handle("heapSnapshot", heapSnapshot)
.handle("agentRequirements", agentRequirements)
.handle("commandFiles", commandFiles)
.handle("removeCommand", removeCommand)
.handle("removeSkill", removeSkill)
@@ -1,7 +1,6 @@
import { Image } from "@/image/image" // kilocode_change - classify user image validation defects
import { busyMessage, isBusy } from "@/kilocode/database/sqlite-error" // kilocode_change
import { KiloSessionHttpApi } from "@/kilocode/server/httpapi/session-fork" // kilocode_change
import { BlockedError as AgentRequirementError } from "@/kilocode/agent-requirements" // kilocode_change
import { KiloSessionPromptQueue } from "@/kilocode/session/prompt-queue" // kilocode_change
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { KiloViewers } from "@/kilocode/presence/service" // kilocode_change
@@ -342,9 +341,7 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
// kilocode_change end
yield* events.publish(Session.Event.Error, {
sessionID: ctx.params.sessionID,
error: AgentRequirementError.isInstance(error)
? error.toObject()
: busy // kilocode_change
error: busy // kilocode_change
? new NamedError.Unknown({ message: busyMessage }).toObject() // kilocode_change
: new NamedError.Unknown({ message: Cause.pretty(cause) }).toObject(), // kilocode_change
})
+2 -12
View File
@@ -138,18 +138,14 @@ function isOrphanedInterruptedTool(part: SessionV1.ToolPart) {
export interface Interface {
readonly cancel: (sessionID: SessionID) => Effect.Effect<void>
// kilocode_change start - prompt can fail on unmet agent requirements
readonly prompt: (
input: PromptInput,
) => Effect.Effect<SessionV1.WithParts, Image.Error | Agent.RequirementBlockedError>
// kilocode_change end
) => Effect.Effect<SessionV1.WithParts, Image.Error>
readonly loop: (input: LoopInput) => Effect.Effect<SessionV1.WithParts>
readonly shell: (input: ShellInput) => Effect.Effect<SessionV1.WithParts, Session.BusyError>
// kilocode_change start - commands can fail on unmet agent requirements or resume errors
readonly command: (
input: CommandInput,
) => Effect.Effect<SessionV1.WithParts, Image.Error | Agent.RequirementBlockedError | Error>
// kilocode_change end
) => Effect.Effect<SessionV1.WithParts, Image.Error | Error>
readonly resolvePromptParts: (template: string) => Effect.Effect<PromptInput["parts"]>
}
@@ -825,8 +821,6 @@ export const layer = Layer.effect(
yield* events.publish(Session.Event.Error, { sessionID: input.sessionID, error: error.toObject() })
throw error
}
yield* agents.guardRequirements(ag) // kilocode_change - enforce requirements before creating a turn
const model = input.model ?? ag.model ?? (yield* currentModel(input.sessionID))
// kilocode_change start - retain the source session variant across Agent Manager's model-less fork handoff
const stored = !input.model && !ag.model ? model : undefined
@@ -1984,8 +1978,6 @@ export const layer = Layer.effect(
yield* events.publish(Session.Event.Error, { sessionID: input.cmdInput.sessionID, error: error.toObject() })
return yield* Effect.fail(error)
}
yield* agents.guardRequirements(agent)
// Resolve model
const model = yield* Effect.gen(function* () {
if (input.cmdInput.model) return Provider.parseModel(input.cmdInput.model)
@@ -2408,8 +2400,6 @@ export const layer = Layer.effect(
yield* events.publish(Session.Event.Error, { sessionID: input.sessionID, error: error.toObject() })
throw error
}
yield* agents.guardRequirements(agent) // kilocode_change - command agent overrides must satisfy requirements
// kilocode_change start
const variant = KiloWorkflowVariant.resolve({
command: cmd,
+1 -3
View File
@@ -43,7 +43,6 @@ import { KiloSessionEvent } from "@/kilocode/session/event"
import { SessionExport } from "@/kilocode/session-export"
import * as SandboxPolicy from "@/kilocode/sandbox/policy"
import { carryForkDiff } from "@/kilocode/session-portability/cumulative-diff" // kilocode_change
import { BlockedError as AgentRequirementError } from "@/kilocode/agent-requirements"
// kilocode_change end
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
@@ -399,8 +398,7 @@ export const Event = {
sessionID: Schema.optional(SessionID),
// Reuses SessionV1.Assistant.fields.error (already Schema.optional) so
// the derived schema keeps the same discriminated-union shape on the event stream.
// kilocode_change - carry pre-message requirement failures over session.error
error: Schema.optional(Schema.Union([SessionV1.Assistant.fields.error, AgentRequirementError.EffectSchema])),
error: SessionV1.Assistant.fields.error,
},
}),
// kilocode_change start
@@ -1,5 +1,6 @@
import { test, expect, describe, afterEach, beforeEach, spyOn } from "bun:test"
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
import { ConfigAgentV1 } from "@opencode-ai/core/v1/config/agent" // kilocode_change
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
import { Cause, Effect, Exit, Layer, Option } from "effect"
@@ -1585,6 +1586,23 @@ test("config parser preserves permission order while rejecting unknown top-level
}
})
// kilocode_change start - preserve legacy agent requirement declarations without forwarding them
test("config parser ignores legacy agent requirements", () => {
const config = ConfigParse.schema(
ConfigAgentV1.Info,
{
name: "demo",
requirements: { skills: ["needed"] },
custom: true,
},
"agent/demo.md",
)
expect(config.options).toEqual({ custom: true })
expect(config.options).not.toHaveProperty("requirements")
})
// kilocode_change end
// MCP config merging tests
// kilocode_change start - regression for `env` alias on local MCP entries
@@ -1,176 +0,0 @@
import { describe, expect, test } from "bun:test"
import type { AgentRequirementResult } from "@kilocode/sdk/v2"
import { formatError as formatStreamError } from "@/cli/cmd/run/session-data"
import * as Requirements from "@/kilocode/cli/agent-requirements"
const dir = "/tmp/agent-requirements-cli"
function result(input: Partial<AgentRequirementResult> = {}): AgentRequirementResult {
return {
agent: "demo",
directory: dir,
enabled: true,
state: "ready",
skills: [],
mcps: [],
vscode_extensions: [],
...input,
}
}
function client(value: AgentRequirementResult): Requirements.Client {
return {
kilocode: {
agentRequirements: async (parameters, options) => {
expect(parameters).toEqual({ agent: value.agent, directory: value.directory })
expect(options).toEqual({ throwOnError: true })
return { data: value }
},
},
}
}
async function check(value: AgentRequirementResult) {
return Requirements.check({ client: client(value), agent: value.agent, directory: value.directory })
}
describe("CLI agent requirements", () => {
test("allows disabled requirements", async () => {
const output = await check(result({ enabled: false, state: "disabled" }))
expect(output.ok).toBe(true)
})
test("allows ready requirements", async () => {
const output = await check(
result({
skills: [{ name: "ready-skill", status: "ready" }],
mcps: [{ name: "ready-mcp", status: "ready" }],
}),
)
expect(output.ok).toBe(true)
})
test("blocks missing skills", async () => {
const output = await check(
result({
state: "blocked",
skills: [
{ name: "ready-skill", status: "ready" },
{ name: "missing-skill", status: "missing" },
],
}),
)
expect(output.ok).toBe(false)
if (output.ok) return
expect(output.error).toMatchObject({
name: "AgentRequirementError",
data: { agent: "demo", state: "blocked" },
})
expect(output.error.data.skills).toContainEqual({ name: "missing-skill", status: "missing" })
})
test("blocks missing and error MCPs", async () => {
const output = await check(
result({
state: "blocked",
mcps: [
{ name: "missing-mcp", status: "missing" },
{ name: "error-mcp", status: "error", message: "server crashed" },
],
}),
)
expect(output.ok).toBe(false)
if (output.ok) return
expect(output.error.data.mcps).toEqual([
{ name: "missing-mcp", status: "missing" },
{ name: "error-mcp", status: "error", message: "server crashed" },
])
})
test("blocks VS Code extension declarations", async () => {
const output = await check(
result({
vscode_extensions: [{ name: "Sample Extension", id: "publisher.extension" }],
}),
)
expect(output.ok).toBe(false)
if (output.ok) return
expect(output.error.data.state).toBe("blocked")
expect(output.error.data.vscode_extensions).toEqual([{ name: "Sample Extension", id: "publisher.extension" }])
const text = Requirements.format(output.error)
expect(text).toContain("Use the Kilo VS Code extension instead")
expect(text).not.toContain("Install the required skills and configure or connect the required MCP servers")
})
test("formats grouped actionable guidance", () => {
const text = Requirements.format(
Requirements.toError(
result({
state: "error",
error: { code: "discovery_failed", message: "skill scan failed" },
skills: [
{ name: "ready-skill", status: "ready" },
{ name: "missing-skill", status: "missing" },
{ name: "error-skill", status: "error", message: "skill scan failed" },
],
mcps: [
{ name: "missing-mcp", status: "missing" },
{ name: "error-mcp", status: "error", message: "auth failed" },
],
vscode_extensions: [{ name: "Sample Extension", id: "publisher.extension" }],
}),
),
)
expect(text).toContain('Agent requirements are not met for "demo".')
expect(text).toContain("VS Code extensions:")
expect(text).toContain("Sample Extension (publisher.extension)")
expect(text).toContain("Skills:")
expect(text).toContain("missing-skill (missing)")
expect(text).toContain("error-skill (error: skill scan failed)")
expect(text).not.toContain("ready-skill (ready)")
expect(text).toContain("MCP servers:")
expect(text).toContain("missing-mcp (missing)")
expect(text).toContain("error-mcp (error: auth failed)")
expect(text).toContain("Install the required skills and configure or connect the required MCP servers, then retry.")
})
test("recognizes AgentRequirementError objects", () => {
const error = Requirements.toError(result({ state: "blocked" }))
expect(Requirements.data(error)).toEqual(error.data)
expect(Requirements.data({ name: "OtherError", data: error.data })).toBeUndefined()
})
test("stores grouped guidance on AgentRequirementError message", () => {
const error = Requirements.toError(
result({
state: "blocked",
skills: [{ name: "missing-skill", status: "missing" }],
}),
)
const text = Requirements.format(error)
expect(text).toContain('Agent requirements are not met for "demo".')
expect(text).toContain("Skills:")
expect(text).toContain("missing-skill (missing)")
expect(error.data.message).toBe(text)
})
test("formats AgentRequirementError in interactive session errors", () => {
const error = Requirements.toError(
result({
state: "blocked",
mcps: [{ name: "missing-mcp", status: "missing" }],
}),
)
const text = Requirements.format(error)
expect(formatStreamError(error)).toBe(text)
})
})
@@ -1,307 +0,0 @@
import { describe, expect, test } from "bun:test"
import { Cause, Effect, Exit } from "effect"
import { ConfigAgentV1 } from "@opencode-ai/core/v1/config/agent"
import { ConfigParse } from "@/config/parse"
import * as AgentRequirements from "@/kilocode/agent-requirements"
import type { MCP } from "@/mcp"
import type { Skill } from "@/skill"
type Agent = {
name: string
requirements?: unknown
}
type Input = {
active?: boolean
agents?: Record<string, Agent>
skills?: string[]
skillError?: string
mcp?: Record<string, MCP.Status>
mcpError?: string
}
const dir = "/tmp/agent-requirements"
function services(input: Input = {}) {
return {
config: {
get: () => Effect.succeed({ experimental: input.active ? { agent_requirements: true } : {} }),
},
agents: {
get: (name: string) => Effect.succeed(input.agents?.[name]),
},
skills: {
all: () => {
if (input.skillError) return Effect.die(new Error(input.skillError))
const skills = (input.skills ?? []).map(
(name) => ({ name, location: "test", content: "" }) satisfies Skill.Info,
)
return Effect.succeed(skills)
},
},
mcp: {
status: () => {
if (input.mcpError) return Effect.die(new Error(input.mcpError))
return Effect.succeed(input.mcp ?? {})
},
},
}
}
function status(name: string, input: Input) {
return Effect.runPromise(AgentRequirements.status({ ...services(input), name, directory: dir }))
}
async function client(value: string | undefined, run: () => Promise<void>) {
const prev = process.env.KILO_CLIENT
try {
if (value === undefined) delete process.env.KILO_CLIENT
if (value !== undefined) process.env.KILO_CLIENT = value
await run()
} finally {
if (prev === undefined) delete process.env.KILO_CLIENT
if (prev !== undefined) process.env.KILO_CLIENT = prev
}
}
describe("agent requirements", () => {
test("returns disabled when the experimental flag is absent", async () => {
const result = await status("missing", {})
expect(result).toMatchObject({
agent: "missing",
directory: dir,
enabled: false,
state: "disabled",
skills: [],
mcps: [],
vscode_extensions: [],
})
})
test("returns ready for agents without requirements", async () => {
const result = await status("demo", { active: true, agents: { demo: { name: "demo" } } })
expect(result).toMatchObject({ agent: "demo", directory: dir, enabled: true, state: "ready" })
})
test("reports unknown agents while requirements are enabled", async () => {
const result = await status("missing", { active: true })
expect(result.state).toBe("error")
expect(result.error?.code).toBe("unknown_agent")
})
test("reports discovered and missing skills", async () => {
const result = await status("demo", {
active: true,
agents: { demo: { name: "demo", requirements: { skills: ["ready", "absent"] } } },
skills: ["ready"],
})
expect(result.state).toBe("blocked")
expect(result.skills).toEqual([
{ name: "ready", status: "ready" },
{ name: "absent", status: "missing" },
])
})
test("accepts non-empty marketplace skill and MCP IDs", async () => {
const result = await status("demo", {
active: true,
agents: {
demo: {
name: "demo",
requirements: { skills: ["skill with space"], mcps: ["mcp/with/slash"] },
},
},
skills: ["skill with space"],
mcp: { "mcp/with/slash": { status: "connected" } },
})
expect(result.state).toBe("ready")
expect(result.skills).toEqual([{ name: "skill with space", status: "ready" }])
expect(result.mcps).toEqual([{ name: "mcp/with/slash", status: "ready" }])
})
test("reports skill discovery failures as errors", async () => {
const result = await status("demo", {
active: true,
agents: { demo: { name: "demo", requirements: { skills: ["needed"] } } },
skillError: "skill scan failed",
})
expect(result.state).toBe("error")
expect(result.error?.code).toBe("discovery_failed")
expect(result.skills[0]?.status).toBe("error")
expect(result.skills[0]?.message).toContain("skill scan failed")
})
test("reports malformed and duplicate declarations", async () => {
const empty = await status("empty", {
active: true,
agents: { empty: { name: "empty", requirements: {} } },
})
const duplicate = await status("duplicate", {
active: true,
agents: { duplicate: { name: "duplicate", requirements: { skills: ["one", "one"] } } },
})
const legacy = await status("legacy", {
active: true,
agents: { legacy: { name: "legacy", requirements: { vscode_extensions: ["publisher.extension"] } } },
})
expect(empty.error?.code).toBe("malformed_declaration")
expect(duplicate.error?.code).toBe("malformed_declaration")
expect(legacy.error?.code).toBe("malformed_declaration")
})
test("validates VS Code extension objects", async () => {
const valid = await status("valid", {
active: true,
agents: {
valid: {
name: "valid",
requirements: { vscode_extensions: [{ name: "Sample Extension", id: "publisher.extension" }] },
},
},
})
const invalid = await status("invalid", {
active: true,
agents: {
invalid: {
name: "invalid",
requirements: { vscode_extensions: [{ name: " ", id: "publisher.extension" }] },
},
},
})
expect(valid).toMatchObject({
state: "ready",
vscode_extensions: [{ name: "Sample Extension", id: "publisher.extension" }],
})
expect(invalid.state).toBe("error")
expect(invalid.error?.code).toBe("malformed_declaration")
})
test("reports MCP connected, missing, and error states", async () => {
const result = await status("demo", {
active: true,
agents: {
demo: {
name: "demo",
requirements: { mcps: ["connected", "disabled", "failed", "registration"] },
},
},
mcp: {
connected: { status: "connected" },
disabled: { status: "disabled" },
failed: { status: "failed", error: "server crashed" },
registration: { status: "needs_client_registration", error: "OAuth required" },
},
})
expect(result.state).toBe("blocked")
expect(result.mcps).toEqual([
{ name: "connected", status: "ready" },
{ name: "disabled", status: "missing" },
{ name: "failed", status: "error", message: "server crashed" },
{ name: "registration", status: "error", message: "OAuth required" },
])
})
test("reports MCP status service failures", async () => {
const result = await status("demo", {
active: true,
agents: { demo: { name: "demo", requirements: { mcps: ["needed"] } } },
mcpError: "status unavailable",
})
expect(result.state).toBe("error")
expect(result.error?.code).toBe("mcp_status_failed")
expect(result.mcps[0]?.status).toBe("error")
expect(result.mcps[0]?.message).toContain("status unavailable")
})
test("guards unmet requirements for all clients", async () => {
const missing = {
active: true,
agents: { demo: { name: "demo", requirements: { skills: ["missing"] } } },
}
await client("cli", async () => {
const exit = await Effect.runPromiseExit(
AgentRequirements.guard({ ...services(missing), agent: missing.agents.demo, directory: dir }),
)
expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isSuccess(exit)) return
const error = Cause.squash(exit.cause)
expect(AgentRequirements.BlockedError.isInstance(error)).toBe(true)
})
await client("vscode", async () => {
const exit = await Effect.runPromiseExit(
AgentRequirements.guard({ ...services(missing), agent: missing.agents.demo, directory: dir }),
)
expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isSuccess(exit)) return
const error = Cause.squash(exit.cause)
expect(AgentRequirements.BlockedError.isInstance(error)).toBe(true)
})
})
test("blocks VS Code extension requirements outside VS Code", async () => {
const input = {
active: true,
agents: {
demo: {
name: "demo",
requirements: { vscode_extensions: [{ name: "Jupyter", id: "ms-toolsai.jupyter" }] },
},
},
}
await client("cli", async () => {
const exit = await Effect.runPromiseExit(
AgentRequirements.guard({ ...services(input), agent: input.agents.demo, directory: dir }),
)
expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isSuccess(exit)) return
const error = Cause.squash(exit.cause)
expect(AgentRequirements.BlockedError.isInstance(error)).toBe(true)
})
await client("vscode", async () => {
await Effect.runPromise(AgentRequirements.guard({ ...services(input), agent: input.agents.demo, directory: dir }))
})
})
test("allows non-VS Code clients when requirements are ready", async () => {
const input = {
active: true,
agents: { demo: { name: "demo", requirements: { skills: ["ready"], mcps: ["connected"] } } },
skills: ["ready"],
mcp: { connected: { status: "connected" as const } },
}
await client("cli", async () => {
await Effect.runPromise(AgentRequirements.guard({ ...services(input), agent: input.agents.demo, directory: dir }))
})
})
test("keeps requirements out of agent options", () => {
const agent = ConfigParse.schema(
ConfigAgentV1.Info,
{
name: "demo",
requirements: { skills: ["needed"] },
custom: true,
},
"agent/demo.md",
)
expect(agent.requirements).toEqual({ skills: ["needed"] })
expect(agent.options).toEqual({ custom: true })
expect(agent.options).not.toHaveProperty("requirements")
})
})
@@ -556,19 +556,6 @@ export const kiloScenarios: Scenario[] = [
yield* Effect.promise(() => rm(body, { force: true }))
}),
),
http.protected
.get("/kilocode/agent/requirements", "kilocode.agentRequirements")
.at((ctx) => ({ path: "/kilocode/agent/requirements?agent=httpapi-agent", headers: ctx.headers() }))
.json(200, (body, ctx) => {
object(body)
check(body.agent === "httpapi-agent", "agent requirements should echo the requested agent")
check(body.directory === ctx.directory, "agent requirements should use the routed workspace directory")
check(body.enabled === false, "agent requirements should report disabled when the experiment is off")
check(body.state === "disabled", "agent requirements should return the disabled state")
array(body.skills)
array(body.mcps)
array(body.vscode_extensions)
}),
http.protected
.get("/kilocode/command/files", "kilocode.commandFiles")
.inProject({ git: true, init: command })
@@ -74,7 +74,6 @@ const fastAgents = Layer.mock(AgentSvc.Service)({
list: () => Effect.succeed([agent]),
defaultInfo: () => Effect.succeed(agent),
defaultAgent: () => Effect.succeed(agent.name),
guardRequirements: () => Effect.void,
})
const summary = Layer.succeed(
@@ -16,17 +16,6 @@ const agents = Agent.Service.of({
list: () => Effect.succeed([info]),
defaultInfo: () => Effect.succeed(info),
defaultAgent: () => Effect.succeed("code"),
requirementStatus: () =>
Effect.succeed({
agent: "code",
directory: "",
enabled: false,
state: "ready",
skills: [],
mcps: [],
vscode_extensions: [],
}),
guardRequirements: () => Effect.void,
generate: () => Effect.succeed({ identifier: "code", whenToUse: "", systemPrompt: "" }),
})
@@ -20,17 +20,6 @@ const agents = Agent.Service.of({
list: () => Effect.succeed([agentInfo]),
defaultInfo: () => Effect.succeed(agentInfo),
defaultAgent: () => Effect.succeed("code"),
requirementStatus: () =>
Effect.succeed({
agent: "code",
directory: "",
enabled: false,
state: "ready",
skills: [],
mcps: [],
vscode_extensions: [],
}),
guardRequirements: () => Effect.void,
generate: () => Effect.succeed({ identifier: "code", whenToUse: "", systemPrompt: "" }),
})
@@ -26,17 +26,6 @@ const agents = Agent.Service.of({
list: () => Effect.succeed([agentInfo]),
defaultInfo: () => Effect.succeed(agentInfo),
defaultAgent: () => Effect.succeed("code"),
requirementStatus: () =>
Effect.succeed({
agent: "code",
directory: "",
enabled: false,
state: "ready",
skills: [],
mcps: [],
vscode_extensions: [],
}),
guardRequirements: () => Effect.void,
generate: () => Effect.succeed({ identifier: "code", whenToUse: "", systemPrompt: "" }),
})
@@ -189,7 +189,6 @@ const fastAgents = Layer.mock(AgentSvc.Service)({
list: () => Effect.succeed([agent]),
defaultInfo: () => Effect.succeed(agent),
defaultAgent: () => Effect.succeed(agent.name),
guardRequirements: () => Effect.void,
})
const processorCreateStarted: Deferred.Deferred<void>[] = []
-38
View File
@@ -181,8 +181,6 @@ import type {
KilocodeAgentManagerRejectResponses,
KilocodeAgentManagerReplyErrors,
KilocodeAgentManagerReplyResponses,
KilocodeAgentRequirementsErrors,
KilocodeAgentRequirementsResponses,
KilocodeCommandFilesErrors,
KilocodeCommandFilesResponses,
KilocodeHeapSnapshotErrors,
@@ -8074,42 +8072,6 @@ export class SessionImport extends HeyApiClient {
}
export class Kilocode extends HeyApiClient {
/**
* Check agent requirements
*
* Check whether the selected agent's requirements are available in the request directory.
*/
public agentRequirements<ThrowOnError extends boolean = false>(
parameters: {
directory?: string
workspace?: string
agent: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "query", key: "agent" },
],
},
],
)
return (options?.client ?? this.client).get<
KilocodeAgentRequirementsResponses,
KilocodeAgentRequirementsErrors,
ThrowOnError
>({
url: "/kilocode/agent/requirements",
...options,
...params,
})
}
/**
* List command files
*
-56
View File
@@ -2707,7 +2707,6 @@ export type Config = {
batch_tool?: boolean
image_generation?: boolean
image_generation_model?: string
agent_requirements?: boolean
native_notebook_tools?: boolean
speech_to_text_model?: string
openTelemetry?: boolean
@@ -4165,31 +4164,6 @@ export type CloudSessionImportError = {
error: string
}
export type AgentRequirementResult = {
agent: string
directory: string
enabled: boolean
state: "disabled" | "ready" | "blocked" | "error"
skills: Array<{
name: string
status: "ready" | "missing" | "error"
message?: string
}>
mcps: Array<{
name: string
status: "ready" | "missing" | "error"
message?: string
}>
vscode_extensions: Array<{
name: string
id: string
}>
error?: {
code: "unknown_agent" | "malformed_declaration" | "discovery_failed" | "mcp_status_failed"
message: string
}
}
export type CommandFile = {
name: string
description?: string
@@ -16542,36 +16516,6 @@ export type KilocodeHeapSnapshotResponses = {
export type KilocodeHeapSnapshotResponse = KilocodeHeapSnapshotResponses[keyof KilocodeHeapSnapshotResponses]
export type KilocodeAgentRequirementsData = {
body?: never
path?: never
query: {
directory?: string
workspace?: string
agent: string
}
url: "/kilocode/agent/requirements"
}
export type KilocodeAgentRequirementsErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type KilocodeAgentRequirementsError = KilocodeAgentRequirementsErrors[keyof KilocodeAgentRequirementsErrors]
export type KilocodeAgentRequirementsResponses = {
/**
* Agent requirement status
*/
200: AgentRequirementResult
}
export type KilocodeAgentRequirementsResponse =
KilocodeAgentRequirementsResponses[keyof KilocodeAgentRequirementsResponses]
export type KilocodeCommandFilesData = {
body?: never
path?: never
-161
View File
@@ -14930,68 +14930,6 @@
]
}
},
"/kilocode/agent/requirements": {
"get": {
"tags": ["kilocode"],
"operationId": "kilocode.agentRequirements",
"parameters": [
{
"name": "directory",
"in": "query",
"schema": {
"type": "string"
},
"required": false
},
{
"name": "workspace",
"in": "query",
"schema": {
"type": "string"
},
"required": false
},
{
"name": "agent",
"in": "query",
"schema": {
"type": "string"
},
"required": true
}
],
"responses": {
"200": {
"description": "Agent requirement status",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AgentRequirementResult"
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"description": "Check whether the selected agent's requirements are available in the request directory.",
"summary": "Check agent requirements",
"x-codeSamples": [
{
"lang": "js",
"source": "import { createKiloClient } from \"@kilocode/sdk\"\n\nconst client = createKiloClient()\nawait client.kilocode.agentRequirements({\n ...\n})"
}
]
}
},
"/kilocode/command/files": {
"get": {
"tags": ["kilocode"],
@@ -34036,9 +33974,6 @@
"image_generation_model": {
"type": "string"
},
"agent_requirements": {
"type": "boolean"
},
"native_notebook_tools": {
"type": "boolean"
},
@@ -38566,102 +38501,6 @@
"required": ["error"],
"additionalProperties": false
},
"AgentRequirementResult": {
"type": "object",
"properties": {
"agent": {
"type": "string"
},
"directory": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"state": {
"type": "string",
"enum": ["disabled", "ready", "blocked", "error"]
},
"skills": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"type": "string",
"enum": ["ready", "missing", "error"]
},
"message": {
"type": "string"
}
},
"required": ["name", "status"],
"additionalProperties": false
}
},
"mcps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"type": "string",
"enum": ["ready", "missing", "error"]
},
"message": {
"type": "string"
}
},
"required": ["name", "status"],
"additionalProperties": false
}
},
"vscode_extensions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "\\S"
},
"id": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
}
},
"required": ["name", "id"],
"additionalProperties": false
}
},
"error": {
"type": "object",
"properties": {
"code": {
"type": "string",
"enum": ["unknown_agent", "malformed_declaration", "discovery_failed", "mcp_status_failed"]
},
"message": {
"type": "string"
}
},
"required": ["code", "message"],
"additionalProperties": false
}
},
"required": ["agent", "directory", "enabled", "state", "skills", "mcps", "vscode_extensions"],
"additionalProperties": false
},
"CommandFile": {
"type": "object",
"properties": {
@@ -55,7 +55,6 @@ import { useArgs } from "../../context/args"
// kilocode_change start
import { KiloSessionTuiSync } from "@/kilocode/session/tui-sync"
import { slashMatches } from "@/kilocode/cli/cmd/command-display"
import * as AgentRequirements from "@/kilocode/cli/agent-requirements"
import { createCostAlertController } from "@/kilocode/cli/cmd/tui/cost-alert"
import { MemoryPrompt } from "@/kilocode/cli/cmd/tui/component/memory-prompt"
// kilocode_change end
@@ -1087,29 +1086,6 @@ export function Prompt(props: PromptProps) {
return false
}
// kilocode_change start - gate TUI sends on declared agent requirements
const requirements = await AgentRequirements.check({
client: sdk.client,
agent: agent.name,
directory: project.instance.directory() || sdk.directory || process.cwd(),
}).catch((error) => {
toast.show({
message: errorMessage(error),
variant: "error",
})
return undefined
})
if (!requirements) return false
if (!requirements.ok) {
toast.show({
title: "Agent requirements",
message: requirements.error.data.message,
variant: "error",
})
return false
}
// kilocode_change end
const workspaceSession = props.sessionID ? sync.session.get(props.sessionID) : undefined
const workspaceID = workspaceSession?.workspaceID
const workspaceStatus = workspaceID ? (project.workspace.status(workspaceID) ?? "error") : undefined