Normalize instance lifecycle wiring (#25501)
This commit is contained in:
@@ -3,6 +3,7 @@ import { Effect } from "effect"
|
||||
import path from "path"
|
||||
import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { WithInstance } from "../../src/project/with-instance"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { Permission } from "../../src/permission"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
@@ -23,7 +24,7 @@ afterEach(async () => {
|
||||
|
||||
test("returns default native agents when no config", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agents = await load(tmp.path, (svc) => svc.list())
|
||||
@@ -41,7 +42,7 @@ test("returns default native agents when no config", async () => {
|
||||
|
||||
test("build agent has correct default properties", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -56,7 +57,7 @@ test("build agent has correct default properties", async () => {
|
||||
|
||||
test("plan agent denies edits except .opencode/plans/*", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const plan = await load(tmp.path, (svc) => svc.get("plan"))
|
||||
@@ -71,7 +72,7 @@ test("plan agent denies edits except .opencode/plans/*", async () => {
|
||||
|
||||
test("explore agent denies edit and write", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const explore = await load(tmp.path, (svc) => svc.get("explore"))
|
||||
@@ -87,7 +88,7 @@ test("explore agent denies edit and write", async () => {
|
||||
test("explore agent asks for external directories and allows whitelisted external paths", async () => {
|
||||
const { Truncate } = await import("../../src/tool/truncate")
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const explore = await load(tmp.path, (svc) => svc.get("explore"))
|
||||
@@ -103,7 +104,7 @@ test("explore agent asks for external directories and allows whitelisted externa
|
||||
|
||||
test("general agent denies todo tools", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const general = await load(tmp.path, (svc) => svc.get("general"))
|
||||
@@ -117,7 +118,7 @@ test("general agent denies todo tools", async () => {
|
||||
|
||||
test("compaction agent denies all permissions", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const compaction = await load(tmp.path, (svc) => svc.get("compaction"))
|
||||
@@ -143,7 +144,7 @@ test("custom agent from config creates new agent", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const custom = await load(tmp.path, (svc) => svc.get("my_custom_agent"))
|
||||
@@ -172,7 +173,7 @@ test("custom agent config overrides native agent properties", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -195,7 +196,7 @@ test("agent disable removes agent from list", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const explore = await load(tmp.path, (svc) => svc.get("explore"))
|
||||
@@ -221,7 +222,7 @@ test("agent permission config merges with defaults", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -242,7 +243,7 @@ test("global permission config applies to all agents", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -261,7 +262,7 @@ test("agent steps/maxSteps config sets steps property", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -280,7 +281,7 @@ test("agent mode can be overridden", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const explore = await load(tmp.path, (svc) => svc.get("explore"))
|
||||
@@ -297,7 +298,7 @@ test("agent name can be overridden", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -314,7 +315,7 @@ test("agent prompt can be set from config", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -334,7 +335,7 @@ test("unknown agent properties are placed into options", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -357,7 +358,7 @@ test("agent options merge correctly", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -382,7 +383,7 @@ test("multiple custom agents can be defined", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agentA = await load(tmp.path, (svc) => svc.get("agent_a"))
|
||||
@@ -411,7 +412,7 @@ test("Agent.list keeps the default agent first and sorts the rest by name", asyn
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const names = (await load(tmp.path, (svc) => svc.list())).map((a) => a.name)
|
||||
@@ -423,7 +424,7 @@ test("Agent.list keeps the default agent first and sorts the rest by name", asyn
|
||||
|
||||
test("Agent.get returns undefined for non-existent agent", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const nonExistent = await load(tmp.path, (svc) => svc.get("does_not_exist"))
|
||||
@@ -434,7 +435,7 @@ test("Agent.get returns undefined for non-existent agent", async () => {
|
||||
|
||||
test("default permission includes doom_loop and external_directory as ask", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -446,7 +447,7 @@ test("default permission includes doom_loop and external_directory as ask", asyn
|
||||
|
||||
test("webfetch is allowed by default", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -468,7 +469,7 @@ test("legacy tools config converts to permissions", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -490,7 +491,7 @@ test("legacy tools config maps write/edit/patch to edit permission", async () =>
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -508,7 +509,7 @@ test("Truncate.GLOB is allowed even when user denies external_directory globally
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -521,7 +522,7 @@ test("Truncate.GLOB is allowed even when user denies external_directory globally
|
||||
|
||||
test("global tmp directory children are allowed for external_directory", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -546,7 +547,7 @@ test("Truncate.GLOB is allowed even when user denies external_directory per-agen
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -569,7 +570,7 @@ test("explicit Truncate.GLOB deny is respected", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -601,7 +602,7 @@ description: Permission skill.
|
||||
process.env.OPENCODE_TEST_HOME = tmp.path
|
||||
|
||||
try {
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const build = await load(tmp.path, (svc) => svc.get("build"))
|
||||
@@ -617,7 +618,7 @@ description: Permission skill.
|
||||
|
||||
test("defaultAgent returns build when no default_agent config", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await load(tmp.path, (svc) => svc.defaultAgent())
|
||||
@@ -632,7 +633,7 @@ test("defaultAgent respects default_agent config set to plan", async () => {
|
||||
default_agent: "plan",
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await load(tmp.path, (svc) => svc.defaultAgent())
|
||||
@@ -652,7 +653,7 @@ test("defaultAgent respects default_agent config set to custom agent with mode a
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await load(tmp.path, (svc) => svc.defaultAgent())
|
||||
@@ -667,7 +668,7 @@ test("defaultAgent throws when default_agent points to subagent", async () => {
|
||||
default_agent: "explore",
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow('default agent "explore" is a subagent')
|
||||
@@ -681,7 +682,7 @@ test("defaultAgent throws when default_agent points to hidden agent", async () =
|
||||
default_agent: "compaction",
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow('default agent "compaction" is hidden')
|
||||
@@ -695,7 +696,7 @@ test("defaultAgent throws when default_agent points to non-existent agent", asyn
|
||||
default_agent: "does_not_exist",
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
await expect(load(tmp.path, (svc) => svc.defaultAgent())).rejects.toThrow(
|
||||
@@ -713,7 +714,7 @@ test("defaultAgent returns plan when build is disabled and default_agent not set
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agent = await load(tmp.path, (svc) => svc.defaultAgent())
|
||||
@@ -732,7 +733,7 @@ test("defaultAgent throws when all primary agents are disabled", async () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
// build and plan are disabled, no primary-capable agents remain
|
||||
|
||||
@@ -4,6 +4,7 @@ import { pathToFileURL } from "url"
|
||||
import { AppRuntime } from "../../src/effect/app-runtime"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { WithInstance } from "../../src/project/with-instance"
|
||||
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -39,7 +40,7 @@ test("plugin-registered agents appear in Agent.list", async () => {
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
await WithInstance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const agents = await AppRuntime.runPromise(Agent.Service.use((svc) => svc.list()))
|
||||
|
||||
Reference in New Issue
Block a user