mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-30 17:14:40 +08:00
Merge pull request #13225 from Kilo-Org/busy-computer
fix: remove agent requirements feature
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Remove the experimental agent requirements check and its configuration flag.
|
||||
@@ -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> => {
|
||||
|
||||
@@ -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",
|
||||
}),
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a093b9658cc15d0789da5afebde7163ec51e743af59975e2a031aed3e9144d56
|
||||
size 22336
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0b20e8e50b583cc6a884e08648a9f1efcdfc2a9561b97145811ebc9c24d244fd
|
||||
size 23100
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3b24b0d2694d2eeec0ea4cd827bd3a61bb47150605b838b43e4f422c9b960f0e
|
||||
size 29744
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fd27610a2518018c16e4227728221ed9bf67a7d3c8ab207b2835dc60964ec63d
|
||||
size 33323
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d8ad46cc736472c6a2a46bb80e6622333f49960e74dcb6782d35aa72fa32378c
|
||||
size 23725
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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": "MCP’er",
|
||||
"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
@@ -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
@@ -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
@@ -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 l’extension 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 l’agent {{agent}}",
|
||||
"agentRequirements.blocked.description": "Cet agent a besoin des outils suivants avant de pouvoir s’exécuter.",
|
||||
"agentRequirements.prompt.blocked": "Terminez d’abord les vérifications requises pour utiliser cet agent",
|
||||
"agentRequirements.action.openMarketplace": "Ouvrir Marketplace",
|
||||
"agentRequirements.error.unknownAgent": "L’agent sélectionné est introuvable.",
|
||||
"agentRequirements.error.malformedDeclaration": "Cet agent a une déclaration de prérequis invalide.",
|
||||
"agentRequirements.error.discoveryFailed": "Kilo n’a pas pu vérifier les compétences disponibles.",
|
||||
"agentRequirements.error.mcpStatusFailed": "Kilo n’a pas pu vérifier l’état du serveur MCP.",
|
||||
"agentRequirements.error.scopeMismatch": "Cette vérification des prérequis de l’agent n’est plus active.",
|
||||
"agentRequirements.error.requestFailed": "Kilo n’a pas pu vérifier les prérequis de l’agent.",
|
||||
|
||||
"dialog.provider.search.placeholder": "Rechercher des fournisseurs",
|
||||
"dialog.provider.empty": "Aucun fournisseur trouvé",
|
||||
"dialog.provider.group.other": "Autre",
|
||||
|
||||
-25
@@ -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 dell’estensione 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 dell’agente {{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 l’agente 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 dell’agente non è più attivo.",
|
||||
"agentRequirements.error.requestFailed": "Kilo non ha potuto controllare i requisiti dell’agente.",
|
||||
|
||||
// Model groups
|
||||
|
||||
// Plan exit
|
||||
|
||||
-24
@@ -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
@@ -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
@@ -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": "MCP’s",
|
||||
"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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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": "MCP’ler",
|
||||
"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": "Marketplace’i 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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>[] = []
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user