Code Agent (#84)
* refactor: rename build agent to code * refactor: add backward compat for build config
This commit is contained in:
@@ -26,7 +26,7 @@ const seed = async () => {
|
||||
sessionID: session.id,
|
||||
role: "user" as const,
|
||||
time: { created: now },
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change - renamed from "build" to "code"
|
||||
model: {
|
||||
providerID,
|
||||
modelID,
|
||||
|
||||
@@ -72,9 +72,11 @@ export namespace Agent {
|
||||
const user = PermissionNext.fromConfig(cfg.permission ?? {})
|
||||
|
||||
const result: Record<string, Info> = {
|
||||
build: {
|
||||
name: "build",
|
||||
// kilocode_change start
|
||||
code: {
|
||||
name: "code",
|
||||
description: "The default agent. Executes tools based on configured permissions.",
|
||||
// kilocode_change end
|
||||
options: {},
|
||||
permission: PermissionNext.merge(
|
||||
defaults,
|
||||
@@ -201,19 +203,23 @@ export namespace Agent {
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(cfg.agent ?? {})) {
|
||||
// kilocode_change start
|
||||
// Treat "build" config as "code" for backward compatibility
|
||||
const effectiveKey = key === "build" ? "code" : key
|
||||
if (value.disable) {
|
||||
delete result[key]
|
||||
delete result[effectiveKey]
|
||||
continue
|
||||
}
|
||||
let item = result[key]
|
||||
let item = result[effectiveKey]
|
||||
if (!item)
|
||||
item = result[key] = {
|
||||
name: key,
|
||||
item = result[effectiveKey] = {
|
||||
name: effectiveKey,
|
||||
mode: "all",
|
||||
permission: PermissionNext.merge(defaults, user),
|
||||
options: {},
|
||||
native: false,
|
||||
}
|
||||
// kilocode_change end
|
||||
if (value.model) item.model = Provider.parseModel(value.model)
|
||||
item.prompt = value.prompt ?? item.prompt
|
||||
item.description = value.description ?? item.description
|
||||
@@ -248,7 +254,10 @@ export namespace Agent {
|
||||
})
|
||||
|
||||
export async function get(agent: string) {
|
||||
return state().then((x) => x[agent])
|
||||
// kilocode_change start - Treat "build" as "code" for backward compatibility
|
||||
const effectiveAgent = agent === "build" ? "code" : agent
|
||||
return state().then((x) => x[effectiveAgent])
|
||||
// kilocode_change end
|
||||
}
|
||||
|
||||
export async function list() {
|
||||
@@ -256,7 +265,7 @@ export namespace Agent {
|
||||
return pipe(
|
||||
await state(),
|
||||
values(),
|
||||
sortBy([(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"]),
|
||||
sortBy([(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "code"), "desc"]), // kilocode_change - renamed from "build" to "code"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -265,8 +274,11 @@ export namespace Agent {
|
||||
const agents = await state()
|
||||
|
||||
if (cfg.default_agent) {
|
||||
const agent = agents[cfg.default_agent]
|
||||
// kilocode_change start - Treat "build" as "code" for backward compatibility
|
||||
const effectiveDefault = cfg.default_agent === "build" ? "code" : cfg.default_agent
|
||||
const agent = agents[effectiveDefault]
|
||||
if (!agent) throw new Error(`default agent "${cfg.default_agent}" not found`)
|
||||
// kilocode_change end
|
||||
if (agent.mode === "subagent") throw new Error(`default agent "${cfg.default_agent}" is a subagent`)
|
||||
if (agent.hidden === true) throw new Error(`default agent "${cfg.default_agent}" is hidden`)
|
||||
return agent.name
|
||||
|
||||
@@ -1013,12 +1013,14 @@ export namespace Config {
|
||||
.string()
|
||||
.describe("Small model to use for tasks like title generation in the format of provider/model")
|
||||
.optional(),
|
||||
// kilocode_change - renamed from "build" to "code"
|
||||
default_agent: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.",
|
||||
"Default agent to use when none is specified. Must be a primary agent. Falls back to 'code' if not set or if the specified agent is invalid.",
|
||||
),
|
||||
// kilocode_change end
|
||||
username: z
|
||||
.string()
|
||||
.optional()
|
||||
|
||||
@@ -54,7 +54,7 @@ export function Tips() {
|
||||
const TIPS = [
|
||||
"Type {highlight}@{/highlight} followed by a filename to fuzzy search and attach files",
|
||||
"Start a message with {highlight}!{/highlight} to run shell commands directly (e.g., {highlight}!ls -la{/highlight})",
|
||||
"Press {highlight}Tab{/highlight} to cycle between Build and Plan agents",
|
||||
"Press {highlight}Tab{/highlight} to cycle between Code and Plan agents",
|
||||
"Use {highlight}/undo{/highlight} to revert the last message and file changes",
|
||||
"Use {highlight}/redo{/highlight} to restore previously undone messages and file changes",
|
||||
"Drag and drop images into the terminal to add them as context",
|
||||
|
||||
@@ -23,7 +23,8 @@ export namespace ModesMigrator {
|
||||
}
|
||||
|
||||
// Default modes to skip - these have native Opencode equivalents
|
||||
const DEFAULT_MODE_SLUGS = new Set(["code", "architect", "ask", "debug", "orchestrator"])
|
||||
// kilocode_change - added "build" for backward compatibility after renaming "build" to "code"
|
||||
const DEFAULT_MODE_SLUGS = new Set(["code", "build", "architect", "ask", "debug", "orchestrator"])
|
||||
|
||||
// Group to permission mapping
|
||||
const GROUP_TO_PERMISSION: Record<string, string> = {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { SystemPrompt } from "./system"
|
||||
import { InstructionPrompt } from "./instruction"
|
||||
import { Plugin } from "../plugin"
|
||||
import PROMPT_PLAN from "../session/prompt/plan.txt"
|
||||
import BUILD_SWITCH from "../session/prompt/build-switch.txt"
|
||||
import CODE_SWITCH from "../session/prompt/code-switch.txt"
|
||||
import MAX_STEPS from "../session/prompt/max-steps.txt"
|
||||
import { defer } from "../util/defer"
|
||||
import { clone } from "remeda"
|
||||
@@ -1214,13 +1214,15 @@ export namespace SessionPrompt {
|
||||
})
|
||||
}
|
||||
const wasPlan = input.messages.some((msg) => msg.info.role === "assistant" && msg.info.agent === "plan")
|
||||
if (wasPlan && input.agent.name === "build") {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
if (wasPlan && input.agent.name === "code") {
|
||||
// kilocode_change end
|
||||
userMessage.parts.push({
|
||||
id: Identifier.ascending("part"),
|
||||
messageID: userMessage.info.id,
|
||||
sessionID: userMessage.info.sessionID,
|
||||
type: "text",
|
||||
text: BUILD_SWITCH,
|
||||
text: CODE_SWITCH,
|
||||
synthetic: true,
|
||||
})
|
||||
}
|
||||
@@ -1241,7 +1243,7 @@ export namespace SessionPrompt {
|
||||
sessionID: userMessage.info.sessionID,
|
||||
type: "text",
|
||||
text:
|
||||
BUILD_SWITCH + "\n\n" + `A plan file exists at ${plan}. You should execute on the plan defined within it`,
|
||||
CODE_SWITCH + "\n\n" + `A plan file exists at ${plan}. You should execute on the plan defined within it`,
|
||||
synthetic: true,
|
||||
})
|
||||
userMessage.parts.push(part)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<system-reminder>
|
||||
Your operational mode has changed from plan to build.
|
||||
Your operational mode has changed from plan to code.
|
||||
You are no longer in read-only mode.
|
||||
You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.
|
||||
</system-reminder>
|
||||
@@ -27,13 +27,15 @@ export const PlanExitTool = Tool.define("plan_exit", {
|
||||
sessionID: ctx.sessionID,
|
||||
questions: [
|
||||
{
|
||||
question: `Plan at ${plan} is complete. Would you like to switch to the build agent and start implementing?`,
|
||||
header: "Build Agent",
|
||||
// kilocode_change start
|
||||
question: `Plan at ${plan} is complete. Would you like to switch to the code agent and start implementing?`,
|
||||
header: "Code Agent",
|
||||
custom: false,
|
||||
options: [
|
||||
{ label: "Yes", description: "Switch to build agent and start implementing the plan" },
|
||||
{ label: "Yes", description: "Switch to code agent and start implementing the plan" },
|
||||
{ label: "No", description: "Stay with plan agent to continue refining the plan" },
|
||||
],
|
||||
// kilocode_change end
|
||||
},
|
||||
],
|
||||
tool: ctx.callID ? { messageID: ctx.messageID, callID: ctx.callID } : undefined,
|
||||
@@ -51,7 +53,7 @@ export const PlanExitTool = Tool.define("plan_exit", {
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change - renamed from "build" to "code"
|
||||
model,
|
||||
}
|
||||
await Session.updateMessage(userMsg)
|
||||
@@ -64,11 +66,13 @@ export const PlanExitTool = Tool.define("plan_exit", {
|
||||
synthetic: true,
|
||||
} satisfies MessageV2.TextPart)
|
||||
|
||||
// kilocode_change start
|
||||
return {
|
||||
title: "Switching to build agent",
|
||||
output: "User approved switching to build agent. Wait for further instructions.",
|
||||
title: "Switching to code agent",
|
||||
output: "User approved switching to code agent. Wait for further instructions.",
|
||||
metadata: {},
|
||||
}
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
|
||||
@@ -88,7 +92,7 @@ export const PlanEnterTool = Tool.define("plan_enter", {
|
||||
custom: false,
|
||||
options: [
|
||||
{ label: "Yes", description: "Switch to plan agent for research and planning" },
|
||||
{ label: "No", description: "Stay with build agent to continue making changes" },
|
||||
{ label: "No", description: "Stay with code agent to continue making changes" }, // kilocode_change - renamed from "build" to "code"
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -159,8 +159,10 @@ function createFakeAgent() {
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
name: "build",
|
||||
description: "build",
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
name: "code",
|
||||
description: "code",
|
||||
// kilocode_change end
|
||||
mode: "agent",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -17,7 +17,7 @@ test("returns default native agents when no config", async () => {
|
||||
fn: async () => {
|
||||
const agents = await Agent.list()
|
||||
const names = agents.map((a) => a.name)
|
||||
expect(names).toContain("build")
|
||||
expect(names).toContain("code") // kilocode_change
|
||||
expect(names).toContain("plan")
|
||||
expect(names).toContain("general")
|
||||
expect(names).toContain("explore")
|
||||
@@ -28,20 +28,22 @@ test("returns default native agents when no config", async () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("build agent has correct default properties", async () => {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
test("code agent has correct default properties", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build).toBeDefined()
|
||||
expect(build?.mode).toBe("primary")
|
||||
expect(build?.native).toBe(true)
|
||||
expect(evalPerm(build, "edit")).toBe("allow")
|
||||
expect(evalPerm(build, "bash")).toBe("allow")
|
||||
const code = await Agent.get("code")
|
||||
expect(code).toBeDefined()
|
||||
expect(code?.mode).toBe("primary")
|
||||
expect(code?.native).toBe(true)
|
||||
expect(evalPerm(code, "edit")).toBe("allow")
|
||||
expect(evalPerm(code, "bash")).toBe("allow")
|
||||
},
|
||||
})
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
test("plan agent denies edits except .opencode/plans/*", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
@@ -137,26 +139,30 @@ test("custom agent config overrides native agent properties", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
// kilocode_change start
|
||||
code: {
|
||||
model: "anthropic/claude-3",
|
||||
description: "Custom build agent",
|
||||
description: "Custom code agent",
|
||||
temperature: 0.7,
|
||||
color: "#FF0000",
|
||||
},
|
||||
// kilocode_change end
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build).toBeDefined()
|
||||
expect(build?.model?.providerID).toBe("anthropic")
|
||||
expect(build?.model?.modelID).toBe("claude-3")
|
||||
expect(build?.description).toBe("Custom build agent")
|
||||
expect(build?.temperature).toBe(0.7)
|
||||
expect(build?.color).toBe("#FF0000")
|
||||
expect(build?.native).toBe(true)
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code).toBeDefined()
|
||||
expect(code?.model?.providerID).toBe("anthropic")
|
||||
expect(code?.model?.modelID).toBe("claude-3")
|
||||
expect(code?.description).toBe("Custom code agent")
|
||||
expect(code?.temperature).toBe(0.7)
|
||||
expect(code?.color).toBe("#FF0000")
|
||||
expect(code?.native).toBe(true)
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -185,7 +191,9 @@ test("agent permission config merges with defaults", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
// kilocode_change start
|
||||
code: {
|
||||
// kilocode_change end
|
||||
permission: {
|
||||
bash: {
|
||||
"rm -rf *": "deny",
|
||||
@@ -198,12 +206,14 @@ test("agent permission config merges with defaults", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build).toBeDefined()
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code).toBeDefined()
|
||||
// Specific pattern is denied
|
||||
expect(PermissionNext.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny")
|
||||
expect(PermissionNext.evaluate("bash", "rm -rf *", code!.permission).action).toBe("deny")
|
||||
// Edit still allowed
|
||||
expect(evalPerm(build, "edit")).toBe("allow")
|
||||
expect(evalPerm(code, "edit")).toBe("allow")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -219,9 +229,11 @@ test("global permission config applies to all agents", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build).toBeDefined()
|
||||
expect(evalPerm(build, "bash")).toBe("deny")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code).toBeDefined()
|
||||
expect(evalPerm(code, "bash")).toBe("deny")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -230,7 +242,9 @@ test("agent steps/maxSteps config sets steps property", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: { steps: 50 },
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: { steps: 50 },
|
||||
// kilocode_change end
|
||||
plan: { maxSteps: 100 },
|
||||
},
|
||||
},
|
||||
@@ -238,9 +252,9 @@ test("agent steps/maxSteps config sets steps property", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
const code = await Agent.get("code") // kilocode_change
|
||||
const plan = await Agent.get("plan")
|
||||
expect(build?.steps).toBe(50)
|
||||
expect(code?.steps).toBe(50) // kilocode_change
|
||||
expect(plan?.steps).toBe(100)
|
||||
},
|
||||
})
|
||||
@@ -267,15 +281,17 @@ test("agent name can be overridden", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: { name: "Builder" },
|
||||
code: { name: "Coder" }, // kilocode_change
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build?.name).toBe("Builder")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code?.name).toBe("Coder")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -284,15 +300,17 @@ test("agent prompt can be set from config", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: { prompt: "Custom system prompt" },
|
||||
code: { prompt: "Custom system prompt" }, // kilocode_change
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build?.prompt).toBe("Custom system prompt")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code?.prompt).toBe("Custom system prompt")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -301,7 +319,7 @@ test("unknown agent properties are placed into options", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
code: {
|
||||
random_property: "hello",
|
||||
another_random: 123,
|
||||
},
|
||||
@@ -311,9 +329,11 @@ test("unknown agent properties are placed into options", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build?.options.random_property).toBe("hello")
|
||||
expect(build?.options.another_random).toBe(123)
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code?.options.random_property).toBe("hello")
|
||||
expect(code?.options.another_random).toBe(123)
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -322,7 +342,9 @@ test("agent options merge correctly", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: {
|
||||
// kilocode_change end
|
||||
options: {
|
||||
custom_option: true,
|
||||
another_option: "value",
|
||||
@@ -334,9 +356,11 @@ test("agent options merge correctly", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(build?.options.custom_option).toBe(true)
|
||||
expect(build?.options.another_option).toBe("value")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(code?.options.custom_option).toBe(true)
|
||||
expect(code?.options.another_option).toBe("value")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -385,9 +409,11 @@ test("default permission includes doom_loop and external_directory as ask", asyn
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(evalPerm(build, "doom_loop")).toBe("ask")
|
||||
expect(evalPerm(build, "external_directory")).toBe("ask")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(evalPerm(code, "doom_loop")).toBe("ask")
|
||||
expect(evalPerm(code, "external_directory")).toBe("ask")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -397,8 +423,10 @@ test("webfetch is allowed by default", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(evalPerm(build, "webfetch")).toBe("allow")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(evalPerm(code, "webfetch")).toBe("allow")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -407,7 +435,9 @@ test("legacy tools config converts to permissions", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: {
|
||||
// kilocode_change end
|
||||
tools: {
|
||||
bash: false,
|
||||
read: false,
|
||||
@@ -419,9 +449,11 @@ test("legacy tools config converts to permissions", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(evalPerm(build, "bash")).toBe("deny")
|
||||
expect(evalPerm(build, "read")).toBe("deny")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(evalPerm(code, "bash")).toBe("deny")
|
||||
expect(evalPerm(code, "read")).toBe("deny")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -430,7 +462,9 @@ test("legacy tools config maps write/edit/patch/multiedit to edit permission", a
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: {
|
||||
// kilocode_change end
|
||||
tools: {
|
||||
write: false,
|
||||
},
|
||||
@@ -441,8 +475,10 @@ test("legacy tools config maps write/edit/patch/multiedit to edit permission", a
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(evalPerm(build, "edit")).toBe("deny")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(evalPerm(code, "edit")).toBe("deny")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -459,10 +495,12 @@ test("Truncate.DIR is allowed even when user denies external_directory globally"
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("deny")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.DIR, code!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.GLOB, code!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", "/some/other/path", code!.permission).action).toBe("deny")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -472,7 +510,9 @@ test("Truncate.DIR is allowed even when user denies external_directory per-agent
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: {
|
||||
// kilocode_change end
|
||||
permission: {
|
||||
external_directory: "deny",
|
||||
},
|
||||
@@ -483,10 +523,12 @@ test("Truncate.DIR is allowed even when user denies external_directory per-agent
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", "/some/other/path", build!.permission).action).toBe("deny")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.DIR, code!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.GLOB, code!.permission).action).toBe("allow")
|
||||
expect(PermissionNext.evaluate("external_directory", "/some/other/path", code!.permission).action).toBe("deny")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -506,20 +548,24 @@ test("explicit Truncate.DIR deny is respected", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.DIR, build!.permission).action).toBe("deny")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.GLOB, build!.permission).action).toBe("deny")
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
const code = await Agent.get("code")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.DIR, code!.permission).action).toBe("deny")
|
||||
expect(PermissionNext.evaluate("external_directory", Truncate.GLOB, code!.permission).action).toBe("deny")
|
||||
// kilocode_change end
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("defaultAgent returns build when no default_agent config", async () => {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
test("defaultAgent returns code when no default_agent config", async () => {
|
||||
// kilocode_change end
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await Agent.defaultAgent()
|
||||
expect(agent).toBe("build")
|
||||
expect(agent).toBe("code") // kilocode_change
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -601,11 +647,15 @@ test("defaultAgent throws when default_agent points to non-existent agent", asyn
|
||||
})
|
||||
})
|
||||
|
||||
test("defaultAgent returns plan when build is disabled and default_agent not set", async () => {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
test("defaultAgent returns plan when code is disabled and default_agent not set", async () => {
|
||||
// kilocode_change end
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: { disable: true },
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: { disable: true },
|
||||
// kilocode_change end
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -613,7 +663,7 @@ test("defaultAgent returns plan when build is disabled and default_agent not set
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await Agent.defaultAgent()
|
||||
// build is disabled, so it should return plan (next primary agent)
|
||||
// kilocode_change - code is disabled, so it should return plan (next primary agent)
|
||||
expect(agent).toBe("plan")
|
||||
},
|
||||
})
|
||||
@@ -623,7 +673,9 @@ test("defaultAgent throws when all primary agents are disabled", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: { disable: true },
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
code: { disable: true },
|
||||
// kilocode_change end
|
||||
plan: { disable: true },
|
||||
},
|
||||
},
|
||||
@@ -631,8 +683,81 @@ test("defaultAgent throws when all primary agents are disabled", async () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
// build and plan are disabled, no primary-capable agents remain
|
||||
// kilocode_change - code and plan are disabled, no primary-capable agents remain
|
||||
await expect(Agent.defaultAgent()).rejects.toThrow("no primary visible agent found")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// kilocode_change start - Backward compatibility tests for "build" -> "code" rename
|
||||
test("Agent.get('build') returns code agent for backward compatibility", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await Agent.get("build")
|
||||
const code = await Agent.get("code")
|
||||
expect(build).toBeDefined()
|
||||
expect(build).toBe(code)
|
||||
expect(build?.name).toBe("code")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("agent.build config applies to code agent for backward compatibility", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: {
|
||||
temperature: 0.8,
|
||||
color: "#00FF00",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const code = await Agent.get("code")
|
||||
expect(code).toBeDefined()
|
||||
expect(code?.temperature).toBe(0.8)
|
||||
expect(code?.color).toBe("#00FF00")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("default_agent: 'build' returns code agent for backward compatibility", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
default_agent: "build",
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await Agent.defaultAgent()
|
||||
expect(agent).toBe("code")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("agent.build disable removes code agent for backward compatibility", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
config: {
|
||||
agent: {
|
||||
build: { disable: true },
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const code = await Agent.get("code")
|
||||
expect(code).toBeUndefined()
|
||||
const agents = await Agent.list()
|
||||
const names = agents.map((a) => a.name)
|
||||
expect(names).not.toContain("code")
|
||||
},
|
||||
})
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
@@ -13,7 +13,7 @@ describe("transcript", () => {
|
||||
id: "msg_123",
|
||||
sessionID: "ses_123",
|
||||
role: "assistant",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
modelID: "claude-sonnet-4-20250514",
|
||||
providerID: "anthropic",
|
||||
mode: "",
|
||||
@@ -26,7 +26,7 @@ describe("transcript", () => {
|
||||
|
||||
test("includes metadata when enabled", () => {
|
||||
const result = formatAssistantHeader(baseMsg, true)
|
||||
expect(result).toBe("## Assistant (Build · claude-sonnet-4-20250514 · 5.4s)\n\n")
|
||||
expect(result).toBe("## Assistant (Code · claude-sonnet-4-20250514 · 5.4s)\n\n") // kilocode_change
|
||||
})
|
||||
|
||||
test("excludes metadata when disabled", () => {
|
||||
@@ -37,7 +37,7 @@ describe("transcript", () => {
|
||||
test("handles missing completed time", () => {
|
||||
const msg = { ...baseMsg, time: { created: 1000000 } }
|
||||
const result = formatAssistantHeader(msg as AssistantMessage, true)
|
||||
expect(result).toBe("## Assistant (Build · claude-sonnet-4-20250514)\n\n")
|
||||
expect(result).toBe("## Assistant (Code · claude-sonnet-4-20250514)\n\n") // kilocode_change
|
||||
})
|
||||
|
||||
test("titlecases agent name", () => {
|
||||
@@ -178,7 +178,7 @@ describe("transcript", () => {
|
||||
id: "msg_123",
|
||||
sessionID: "ses_123",
|
||||
role: "user",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
|
||||
time: { created: 1000000 },
|
||||
}
|
||||
@@ -193,7 +193,7 @@ describe("transcript", () => {
|
||||
id: "msg_123",
|
||||
sessionID: "ses_123",
|
||||
role: "assistant",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
modelID: "claude-sonnet-4-20250514",
|
||||
providerID: "anthropic",
|
||||
mode: "",
|
||||
@@ -205,7 +205,7 @@ describe("transcript", () => {
|
||||
}
|
||||
const parts: Part[] = [{ id: "p1", sessionID: "ses_123", messageID: "msg_123", type: "text", text: "Hi there" }]
|
||||
const result = formatMessage(msg, parts, options)
|
||||
expect(result).toContain("## Assistant (Build · claude-sonnet-4-20250514 · 5.4s)")
|
||||
expect(result).toContain("## Assistant (Code · claude-sonnet-4-20250514 · 5.4s)") // kilocode_change
|
||||
expect(result).toContain("Hi there")
|
||||
})
|
||||
})
|
||||
@@ -223,7 +223,7 @@ describe("transcript", () => {
|
||||
id: "msg_1",
|
||||
sessionID: "ses_abc123",
|
||||
role: "user" as const,
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
|
||||
time: { created: 1000000000000 },
|
||||
},
|
||||
@@ -234,7 +234,7 @@ describe("transcript", () => {
|
||||
id: "msg_2",
|
||||
sessionID: "ses_abc123",
|
||||
role: "assistant" as const,
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
modelID: "claude-sonnet-4-20250514",
|
||||
providerID: "anthropic",
|
||||
mode: "",
|
||||
@@ -255,7 +255,7 @@ describe("transcript", () => {
|
||||
expect(result).toContain("**Session ID:** ses_abc123")
|
||||
expect(result).toContain("## User")
|
||||
expect(result).toContain("Hello")
|
||||
expect(result).toContain("## Assistant (Build · claude-sonnet-4-20250514 · 0.5s)")
|
||||
expect(result).toContain("## Assistant (Code · claude-sonnet-4-20250514 · 0.5s)") // kilocode_change
|
||||
expect(result).toContain("Hi!")
|
||||
expect(result).toContain("---")
|
||||
})
|
||||
@@ -272,7 +272,7 @@ describe("transcript", () => {
|
||||
id: "msg_1",
|
||||
sessionID: "ses_abc123",
|
||||
role: "assistant" as const,
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
modelID: "claude-sonnet-4-20250514",
|
||||
providerID: "anthropic",
|
||||
mode: "",
|
||||
@@ -290,7 +290,7 @@ describe("transcript", () => {
|
||||
const result = formatTranscript(session, messages, options)
|
||||
|
||||
expect(result).toContain("## Assistant\n\n")
|
||||
expect(result).not.toContain("Build")
|
||||
expect(result).not.toContain("Code") // kilocode_change
|
||||
expect(result).not.toContain("claude-sonnet-4-20250514")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ test("agent color parsed from project config", async () => {
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
agent: {
|
||||
build: { color: "#FFA500" },
|
||||
code: { color: "#FFA500" }, // kilocode_change
|
||||
},
|
||||
}),
|
||||
)
|
||||
@@ -24,7 +24,7 @@ test("agent color parsed from project config", async () => {
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const cfg = await Config.get()
|
||||
expect(cfg.agent?.["build"]?.color).toBe("#FFA500")
|
||||
expect(cfg.agent?.["code"]?.color).toBe("#FFA500") // kilocode_change
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ const baseCtx = {
|
||||
sessionID: "test",
|
||||
messageID: "",
|
||||
callID: "",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
abort: AbortSignal.any([]),
|
||||
messages: [],
|
||||
metadata: () => {},
|
||||
|
||||
@@ -10,7 +10,7 @@ const ctx = {
|
||||
sessionID: "test",
|
||||
messageID: "",
|
||||
callID: "",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
abort: AbortSignal.any([]),
|
||||
messages: [],
|
||||
metadata: () => {},
|
||||
|
||||
@@ -9,7 +9,7 @@ const baseCtx: Omit<Tool.Context, "ask"> = {
|
||||
sessionID: "test",
|
||||
messageID: "",
|
||||
callID: "",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
abort: AbortSignal.any([]),
|
||||
messages: [],
|
||||
metadata: () => {},
|
||||
|
||||
@@ -8,7 +8,7 @@ const ctx = {
|
||||
sessionID: "test",
|
||||
messageID: "",
|
||||
callID: "",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
abort: AbortSignal.any([]),
|
||||
messages: [],
|
||||
metadata: () => {},
|
||||
|
||||
@@ -12,7 +12,7 @@ const ctx = {
|
||||
sessionID: "test",
|
||||
messageID: "",
|
||||
callID: "",
|
||||
agent: "build",
|
||||
agent: "code", // kilocode_change
|
||||
abort: AbortSignal.any([]),
|
||||
messages: [],
|
||||
metadata: () => {},
|
||||
@@ -136,7 +136,9 @@ describe("tool.read env file permissions", () => {
|
||||
["environment.ts", false],
|
||||
]
|
||||
|
||||
describe.each(["build", "plan"])("agent=%s", (agentName) => {
|
||||
// kilocode_change start - renamed from "build" to "code"
|
||||
describe.each(["code", "plan"])("agent=%s", (agentName) => {
|
||||
// kilocode_change end
|
||||
test.each(cases)("%s asks=%s", async (filename, shouldAsk) => {
|
||||
await using tmp = await tmpdir({
|
||||
init: (dir) => Bun.write(path.join(dir, filename), "content"),
|
||||
|
||||
@@ -1671,7 +1671,7 @@ export type Config = {
|
||||
*/
|
||||
small_model?: string
|
||||
/**
|
||||
* Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.
|
||||
* Default agent to use when none is specified. Must be a primary agent. Falls back to 'code' if not set or if the specified agent is invalid.
|
||||
*/
|
||||
default_agent?: string
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user