| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- import { describe, expect } from "bun:test"
- import { Effect, Schema } from "effect"
- import { AgentV2 } from "@opencode-ai/core/agent"
- import { Config } from "@opencode-ai/core/config"
- import { ConfigAgentPlugin } from "@opencode-ai/core/config/plugin/agent"
- import { PermissionV2 } from "@opencode-ai/core/permission"
- import { testEffect } from "../lib/effect"
- const it = testEffect(AgentV2.locationLayer)
- const decode = Schema.decodeUnknownSync(Config.Info)
- describe("ConfigAgentPlugin.Plugin", () => {
- it.effect("applies global permissions between built-in and agent-specific permissions", () =>
- Effect.gen(function* () {
- const agents = yield* AgentV2.Service
- const build = AgentV2.ID.make("build")
- const defaults = yield* agents.transform()
- yield* defaults((editor) =>
- editor.update(build, (agent) => {
- agent.mode = "primary"
- agent.permissions.push({ permission: "bash", pattern: "*", action: "allow" })
- }),
- )
- const config = Config.Service.of({
- directories: () => Effect.succeed([]),
- get: () =>
- Effect.succeed([
- new Config.Loaded({
- source: { type: "memory" },
- info: decode({
- permissions: [{ permission: "bash", pattern: "*", action: "ask" }],
- agents: {
- build: {
- permissions: [{ permission: "bash", pattern: "git *", action: "allow" }],
- },
- reviewer: {
- model: "openrouter/openai/gpt-5",
- description: "Review changes",
- mode: "subagent",
- permissions: [{ permission: "edit", pattern: "*", action: "deny" }],
- },
- removed: { description: "Removed later" },
- },
- }),
- }),
- new Config.Loaded({
- source: { type: "memory" },
- info: decode({
- agents: {
- reviewer: { variant: "high", hidden: true },
- removed: { disabled: true },
- },
- }),
- }),
- ]),
- })
- yield* ConfigAgentPlugin.Plugin.effect.pipe(
- Effect.provideService(Config.Service, config),
- Effect.provideService(AgentV2.Service, agents),
- )
- const buildAgent = yield* agents.get(build)
- if (!buildAgent) throw new Error("expected configured build agent")
- expect(buildAgent.permissions).toEqual([
- { permission: "bash", pattern: "*", action: "allow" },
- { permission: "bash", pattern: "*", action: "ask" },
- { permission: "bash", pattern: "git *", action: "allow" },
- ])
- expect(PermissionV2.evaluate("bash", "git status", buildAgent.permissions).action).toBe("allow")
- expect(PermissionV2.evaluate("bash", "bun test", buildAgent.permissions).action).toBe("ask")
- const reviewer = yield* agents.get(AgentV2.ID.make("reviewer"))
- if (!reviewer) throw new Error("expected configured reviewer agent")
- expect(reviewer).toMatchObject({
- description: "Review changes",
- mode: "subagent",
- hidden: true,
- model: { providerID: "openrouter", id: "openai/gpt-5", variant: "high" },
- })
- expect(reviewer.permissions).toEqual([
- { permission: "bash", pattern: "*", action: "ask" },
- { permission: "edit", pattern: "*", action: "deny" },
- ])
- expect(yield* agents.get(AgentV2.ID.make("removed"))).toBeUndefined()
- }),
- )
- it.effect("maps configured agent fields and preserves an unspecified model variant", () =>
- Effect.gen(function* () {
- const agents = yield* AgentV2.Service
- const config = Config.Service.of({
- directories: () => Effect.succeed([]),
- get: () =>
- Effect.succeed([
- new Config.Loaded({
- source: { type: "memory" },
- info: decode({
- agents: {
- reviewer: {
- model: "anthropic/claude-sonnet",
- system: "Review carefully.",
- description: "Reviews changes",
- mode: "subagent",
- hidden: true,
- color: "warning",
- steps: 12,
- options: {
- headers: { first: "one", shared: "first" },
- body: { enabled: true },
- aisdk: { provider: { profile: "review" }, request: { effort: "medium" } },
- },
- },
- },
- }),
- }),
- new Config.Loaded({
- source: { type: "memory" },
- info: decode({
- agents: {
- reviewer: {
- options: {
- headers: { shared: "last", second: "two" },
- body: { retries: 2 },
- aisdk: { request: { effort: "high" } },
- },
- },
- },
- }),
- }),
- ]),
- })
- yield* ConfigAgentPlugin.Plugin.effect.pipe(
- Effect.provideService(Config.Service, config),
- Effect.provideService(AgentV2.Service, agents),
- )
- const reviewer = yield* agents.get(AgentV2.ID.make("reviewer"))
- if (!reviewer) throw new Error("expected configured reviewer agent")
- expect(reviewer).toMatchObject({
- system: "Review carefully.",
- description: "Reviews changes",
- mode: "subagent",
- hidden: true,
- color: "warning",
- steps: 12,
- model: { providerID: "anthropic", id: "claude-sonnet", variant: undefined },
- })
- expect(reviewer.options).toEqual({
- headers: { first: "one", shared: "last", second: "two" },
- body: { enabled: true, retries: 2 },
- aisdk: { provider: { profile: "review" }, request: { effort: "high" } },
- })
- }),
- )
- it.effect("removes a built-in agent disabled by configuration", () =>
- Effect.gen(function* () {
- const agents = yield* AgentV2.Service
- const build = AgentV2.ID.make("build")
- const defaults = yield* agents.transform()
- yield* defaults((editor) => editor.update(build, () => {}))
- const config = Config.Service.of({
- directories: () => Effect.succeed([]),
- get: () =>
- Effect.succeed([
- new Config.Loaded({
- source: { type: "memory" },
- info: decode({ agents: { build: { disabled: true } } }),
- }),
- ]),
- })
- yield* ConfigAgentPlugin.Plugin.effect.pipe(
- Effect.provideService(Config.Service, config),
- Effect.provideService(AgentV2.Service, agents),
- )
- expect(yield* agents.get(build)).toBeUndefined()
- }),
- )
- })
|