This commit is contained in:
Dax Raad 2025-12-17 21:47:40 -05:00
parent 5f50d44de9
commit 291694f32d
3 changed files with 102 additions and 11 deletions

View file

@ -429,7 +429,7 @@ export namespace Config {
temperature: z.number().optional(),
top_p: z.number().optional(),
prompt: z.string().optional(),
tools: z.record(z.string(), z.boolean()).optional(),
tools: z.record(z.string(), z.boolean()).optional().describe("@deprecated Use 'permission' field instead"),
disable: z.boolean().optional(),
description: z.string().optional().describe("Description of when to use the agent"),
mode: z.enum(["subagent", "primary", "all"]).optional(),
@ -449,6 +449,47 @@ export namespace Config {
permission: Permission.optional(),
})
.catchall(z.any())
.transform((agent) => {
const knownKeys = new Set([
"name",
"model",
"prompt",
"description",
"temperature",
"top_p",
"mode",
"color",
"steps",
"maxSteps",
"options",
"permission",
"disable",
"tools",
])
// Extract unknown properties into options
const options: Record<string, unknown> = { ...agent.options }
for (const [key, value] of Object.entries(agent)) {
if (!knownKeys.has(key)) options[key] = value
}
// Convert legacy tools config to permissions
const permission: Permission = { ...agent.permission }
for (const [tool, enabled] of Object.entries(agent.tools ?? {})) {
const action = enabled ? "allow" : "deny"
// write, edit, patch, multiedit all map to edit permission
if (tool === "write" || tool === "edit" || tool === "patch" || tool === "multiedit") {
permission.edit = action
} else {
permission[tool] = action
}
}
return { ...agent, options, permission } as typeof agent & {
options: Record<string, unknown>
permission: Permission
}
})
.meta({
ref: "AgentConfig",
})

View file

@ -389,3 +389,47 @@ test("webfetch is allowed by default", async () => {
},
})
})
test("legacy tools config converts to permissions", async () => {
await using tmp = await tmpdir({
config: {
agent: {
build: {
tools: {
bash: false,
read: false,
},
},
},
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const build = await Agent.get("build")
expect(build?.permission.bash?.["*"]).toBe("deny")
expect(build?.permission.read?.["*"]).toBe("deny")
},
})
})
test("legacy tools config maps write/edit/patch/multiedit to edit permission", async () => {
await using tmp = await tmpdir({
config: {
agent: {
build: {
tools: {
write: false,
},
},
},
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const build = await Agent.get("build")
expect(build?.permission.edit?.["*"]).toBe("deny")
},
})
})

View file

@ -205,11 +205,13 @@ test("handles agent configuration", async () => {
directory: tmp.path,
fn: async () => {
const config = await Config.get()
expect(config.agent?.["test_agent"]).toEqual({
model: "test/model",
temperature: 0.7,
description: "test agent",
})
expect(config.agent?.["test_agent"]).toEqual(
expect.objectContaining({
model: "test/model",
temperature: 0.7,
description: "test agent",
}),
)
},
})
})
@ -292,6 +294,8 @@ test("migrates mode field to agent field", async () => {
model: "test/model",
temperature: 0.5,
mode: "primary",
options: {},
permission: {},
})
},
})
@ -318,11 +322,13 @@ Test agent prompt`,
directory: tmp.path,
fn: async () => {
const config = await Config.get()
expect(config.agent?.["test"]).toEqual({
name: "test",
model: "test/model",
prompt: "Test agent prompt",
})
expect(config.agent?.["test"]).toEqual(
expect.objectContaining({
name: "test",
model: "test/model",
prompt: "Test agent prompt",
}),
)
},
})
})