Prechádzať zdrojové kódy

refactor(effect): simplify task tool registry bridge

Kit Langton 1 týždeň pred
rodič
commit
0f3a2a7b67

+ 3 - 2
packages/opencode/src/session/prompt.ts

@@ -47,6 +47,7 @@ import { Process } from "@/util/process"
 import { Cause, Effect, Exit, Layer, Option, Scope, ServiceMap } from "effect"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
+import { TaskTool } from "@/tool/task"
 
 // @ts-ignore
 globalThis.AI_SDK_LOG_WARNINGS = false
@@ -558,7 +559,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
       }) {
         const { task, model, lastUser, sessionID, session, msgs } = input
         const ctx = yield* InstanceState.context
-        const taskTool = yield* registry.fromID("task")
+        const taskTool = yield* registry.fromID(TaskTool.id)
         const taskModel = task.model ? yield* getModel(task.model.providerID, task.model.modelID, sessionID) : model
         const assistantMessage: MessageV2.Assistant = yield* sessions.updateMessage({
           id: MessageID.ascending(),
@@ -581,7 +582,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
           sessionID: assistantMessage.sessionID,
           type: "tool",
           callID: ulid(),
-          tool: "task",
+          tool: TaskTool.id,
           state: {
             status: "running",
             input: {

+ 42 - 48
packages/opencode/src/tool/registry.ts

@@ -82,18 +82,10 @@ export namespace ToolRegistry {
       const config = yield* Config.Service
       const plugin = yield* Plugin.Service
 
-      const info = <T extends Tool.Info, R = never>(
-        tool: T | Effect.Effect<T, never, R>,
-      ): Effect.Effect<T, never, R> => (Effect.isEffect(tool) ? tool : Effect.succeed(tool))
-
-      const build = <T extends Tool.Info, R = never>(
-        tool: T | Effect.Effect<T, never, R>,
-      ): Effect.Effect<Tool.Def, never, R> => info(tool).pipe(Effect.flatMap(Tool.init))
-
-      const task = yield* info(TaskTool)
-      const read = yield* info(ReadTool)
-      const askInfo = yield* info(QuestionTool)
-      const todoInfo = yield* info(TodoWriteTool)
+      const task = yield* TaskTool
+      const read = yield* ReadTool
+      const question = yield* QuestionTool
+      const todo = yield* TodoWriteTool
 
       const state = yield* InstanceState.make<State>(
         Effect.fn("ToolRegistry.state")(function* (ctx) {
@@ -147,47 +139,49 @@ export namespace ToolRegistry {
           }
 
           const cfg = yield* config.get()
-          const question =
+          const questionEnabled =
             ["app", "cli", "desktop"].includes(Flag.OPENCODE_CLIENT) || Flag.OPENCODE_ENABLE_QUESTION_TOOL
 
-          const invalid = yield* build(InvalidTool)
-          const bash = yield* build(BashTool)
-          const readDef = yield* build(read)
-          const glob = yield* build(GlobTool)
-          const grep = yield* build(GrepTool)
-          const edit = yield* build(EditTool)
-          const write = yield* build(WriteTool)
-          const taskDef = yield* build(task)
-          const fetch = yield* build(WebFetchTool)
-          const todo = yield* build(todoInfo)
-          const search = yield* build(WebSearchTool)
-          const code = yield* build(CodeSearchTool)
-          const skill = yield* build(SkillTool)
-          const patch = yield* build(ApplyPatchTool)
-          const ask = yield* build(askInfo)
-          const lsp = yield* build(LspTool)
-          const plan = yield* build(PlanExitTool)
+          const tool = yield* Effect.all({
+            invalid: Tool.init(InvalidTool),
+            bash: Tool.init(BashTool),
+            read: Tool.init(read),
+            glob: Tool.init(GlobTool),
+            grep: Tool.init(GrepTool),
+            edit: Tool.init(EditTool),
+            write: Tool.init(WriteTool),
+            task: Tool.init(task),
+            fetch: Tool.init(WebFetchTool),
+            todo: Tool.init(todo),
+            search: Tool.init(WebSearchTool),
+            code: Tool.init(CodeSearchTool),
+            skill: Tool.init(SkillTool),
+            patch: Tool.init(ApplyPatchTool),
+            question: Tool.init(question),
+            lsp: Tool.init(LspTool),
+            plan: Tool.init(PlanExitTool),
+          })
 
           return {
             custom,
             builtin: [
-              invalid,
-              ...(question ? [ask] : []),
-              bash,
-              readDef,
-              glob,
-              grep,
-              edit,
-              write,
-              taskDef,
-              fetch,
-              todo,
-              search,
-              code,
-              skill,
-              patch,
-              ...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [lsp] : []),
-              ...(Flag.OPENCODE_EXPERIMENTAL_PLAN_MODE && Flag.OPENCODE_CLIENT === "cli" ? [plan] : []),
+              tool.invalid,
+              ...(questionEnabled ? [tool.question] : []),
+              tool.bash,
+              tool.read,
+              tool.glob,
+              tool.grep,
+              tool.edit,
+              tool.write,
+              tool.task,
+              tool.fetch,
+              tool.todo,
+              tool.search,
+              tool.code,
+              tool.skill,
+              tool.patch,
+              ...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [tool.lsp] : []),
+              ...(Flag.OPENCODE_EXPERIMENTAL_PLAN_MODE && Flag.OPENCODE_CLIENT === "cli" ? [tool.plan] : []),
             ],
           }
         }),
@@ -237,7 +231,7 @@ export namespace ToolRegistry {
               id: tool.id,
               description: [
                 output.description,
-                tool.id === "task" ? yield* TaskDescription(input.agent) : undefined,
+                tool.id === TaskTool.id ? yield* TaskDescription(input.agent) : undefined,
                 tool.id === SkillTool.id ? yield* SkillDescription(input.agent) : undefined,
               ]
                 .filter(Boolean)

+ 6 - 4
packages/opencode/src/tool/task.ts

@@ -10,6 +10,8 @@ import { Config } from "../config/config"
 import { Permission } from "@/permission"
 import { Effect } from "effect"
 
+const id = "task"
+
 const parameters = z.object({
   description: z.string().describe("A short (3-5 words) description of the task"),
   prompt: z.string().describe("The task for the agent to perform"),
@@ -24,7 +26,7 @@ const parameters = z.object({
 })
 
 export const TaskTool = Tool.defineEffect(
-  "task",
+  id,
   Effect.gen(function* () {
     const agent = yield* Agent.Service
     const config = yield* Config.Service
@@ -35,7 +37,7 @@ export const TaskTool = Tool.defineEffect(
       if (!ctx.extra?.bypassAgentCheck) {
         yield* Effect.promise(() =>
           ctx.ask({
-            permission: "task",
+            permission: id,
             patterns: [params.subagent_type],
             always: ["*"],
             metadata: {
@@ -51,7 +53,7 @@ export const TaskTool = Tool.defineEffect(
         return yield* Effect.fail(new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`))
       }
 
-      const canTask = next.permission.some((rule) => rule.permission === "task")
+      const canTask = next.permission.some((rule) => rule.permission === id)
       const canTodo = next.permission.some((rule) => rule.permission === "todowrite")
 
       const taskID = params.task_id
@@ -81,7 +83,7 @@ export const TaskTool = Tool.defineEffect(
                 ? []
                 : [
                     {
-                      permission: "task" as const,
+                      permission: id,
                       pattern: "*" as const,
                       action: "deny" as const,
                     },

+ 11 - 8
packages/opencode/src/tool/tool.ts

@@ -98,24 +98,27 @@ export namespace Tool {
     }
   }
 
-  export function define<Parameters extends z.ZodType, Result extends Metadata>(
-    id: string,
+  export function define<Parameters extends z.ZodType, Result extends Metadata, ID extends string = string>(
+    id: ID,
     init: (() => Promise<DefWithoutID<Parameters, Result>>) | DefWithoutID<Parameters, Result>,
-  ): Info<Parameters, Result> {
+  ): Info<Parameters, Result> & { id: ID } {
     return {
       id,
       init: wrap(id, init),
     }
   }
 
-  export function defineEffect<Parameters extends z.ZodType, Result extends Metadata, R>(
-    id: string,
+  export function defineEffect<Parameters extends z.ZodType, Result extends Metadata, R, ID extends string = string>(
+    id: ID,
     init: Effect.Effect<(() => Promise<DefWithoutID<Parameters, Result>>) | DefWithoutID<Parameters, Result>, never, R>,
-  ): Effect.Effect<Info<Parameters, Result>, never, R> {
-    return Effect.map(init, (next) => ({ id, init: wrap(id, next) }))
+  ): Effect.Effect<Info<Parameters, Result>, never, R> & { id: ID } {
+    return Object.assign(
+      Effect.map(init, (next) => ({ id, init: wrap(id, next) })),
+      { id },
+    )
   }
 
-  export function init(info: Info): Effect.Effect<Def, never, any> {
+  export function init(info: Info): Effect.Effect<Def> {
     return Effect.gen(function* () {
       const init = yield* Effect.promise(() => info.init())
       return {