diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 565c355a6d..7c9dc1cd2b 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -30,7 +30,7 @@ import { Truncate } from "./truncate" import { ApplyPatchTool } from "./apply_patch" import { Glob } from "../util/glob" import { pathToFileURL } from "url" -import { Effect, Layer, ServiceMap } from "effect" +import { Effect, Fiber, Layer, ServiceMap } from "effect" import { InstanceContext } from "@/effect/instance-context" import { runPromiseInstance } from "@/effect/runtime" @@ -55,29 +55,36 @@ export namespace ToolRegistry { const custom: Tool.Info[] = [] - yield* Effect.promise(async () => { - const matches = await Config.directories().then((dirs) => - dirs.flatMap((dir) => - Glob.scanSync("{tool,tools}/*.{js,ts}", { cwd: dir, absolute: true, dot: true, symlink: true }), - ), - ) - if (matches.length) await Config.waitForDependencies() - for (const match of matches) { - const namespace = path.basename(match, path.extname(match)) - const mod = await import(pathToFileURL(match).href) - for (const [id, def] of Object.entries(mod)) { - custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def)) + const load = Effect.fn("ToolRegistry.load")(function* () { + yield* Effect.promise(async () => { + const matches = await Config.directories().then((dirs) => + dirs.flatMap((dir) => + Glob.scanSync("{tool,tools}/*.{js,ts}", { cwd: dir, absolute: true, dot: true, symlink: true }), + ), + ) + if (matches.length) await Config.waitForDependencies() + for (const match of matches) { + const namespace = path.basename(match, path.extname(match)) + const mod = await import(pathToFileURL(match).href) + for (const [id, def] of Object.entries(mod)) { + custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def)) + } } - } - const plugins = await Plugin.list() - for (const plugin of plugins) { - for (const [id, def] of Object.entries(plugin.tool ?? {})) { - custom.push(fromPlugin(id, def)) + const plugins = await Plugin.list() + for (const plugin of plugins) { + for (const [id, def] of Object.entries(plugin.tool ?? {})) { + custom.push(fromPlugin(id, def)) + } } - } + }) }) + const loadFiber = yield* load().pipe( + Effect.catchCause(() => Effect.void), + Effect.forkScoped, + ) + function fromPlugin(id: string, def: ToolDefinition): Tool.Info { return { id, @@ -131,6 +138,7 @@ export namespace ToolRegistry { } const register = Effect.fn("ToolRegistry.register")(function* (tool: Tool.Info) { + yield* Fiber.join(loadFiber) const idx = custom.findIndex((t) => t.id === tool.id) if (idx >= 0) { custom.splice(idx, 1, tool) @@ -140,6 +148,7 @@ export namespace ToolRegistry { }) const ids = Effect.fn("ToolRegistry.ids")(function* () { + yield* Fiber.join(loadFiber) const tools = yield* Effect.promise(() => all()) return tools.map((t) => t.id) }) @@ -148,6 +157,7 @@ export namespace ToolRegistry { model: { providerID: ProviderID; modelID: ModelID }, agent?: Agent.Info, ) { + yield* Fiber.join(loadFiber) const allTools = yield* Effect.promise(() => all()) return yield* Effect.promise(() => Promise.all(