From eaa1350b1de961068ca01156fa0c8476ea24610e Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 13 Jan 2026 20:31:05 -0500 Subject: [PATCH 1/5] feat(plugin): align plugin tool types with built-in tool capabilities Fix plugin tool types to expose ToolContext fields and ExecuteResult that built-in tools already have access to. The only functional change is allowing plugin tools to return structured result objects (with title, metadata, attachments) instead of only plain strings. --- packages/opencode/src/tool/registry.ts | 7 ++++++- packages/plugin/src/tool.ts | 28 ++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 82bf7f56328..853d344aa32 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -65,7 +65,12 @@ export namespace ToolRegistry { parameters: z.object(def.args), description: def.description, execute: async (args, ctx) => { - const result = await def.execute(args as any, ctx) + let result = await def.execute(args as any, ctx) + + if (typeof result !== 'string') { + return result + } + const out = await Truncate.output(result, {}, initCtx?.agent) return { title: "", diff --git a/packages/plugin/src/tool.ts b/packages/plugin/src/tool.ts index 37e802ac408..e21e3531eae 100644 --- a/packages/plugin/src/tool.ts +++ b/packages/plugin/src/tool.ts @@ -1,17 +1,37 @@ import { z } from "zod" +import type { FilePart } from "@opencode-ai/sdk" -export type ToolContext = { +type Metadata = { + [key: string]: any +} + +export type ToolContext = { sessionID: string messageID: string agent: string abort: AbortSignal + callID?: string + extra?: M + metadata(input: { title?: string; metadata?: M }): void + ask(input: { permission: string; patterns: string[]; metadata: M; always: string[] }): Promise +} + +export type ExecuteResult = { + title: string + metadata: M + output: string + attachments?: FilePart[] } -export function tool(input: { +// NB: align with ReturnType in packages/opencode/src/tool/tool.ts +export type Input = { description: string args: Args - execute(args: z.infer>, context: ToolContext): Promise -}) { + execute(args: z.infer>, context: ToolContext): Promise> + formatValidationError?(error: z.ZodError): string +} + +export function tool(input: Input) { return input } tool.schema = z From 07ff5762180030a20faf9d22a93a54cad7b3cfb0 Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 13 Jan 2026 21:12:26 -0500 Subject: [PATCH 2/5] Ensure types are exported so plugin devs can import them if needed --- packages/plugin/src/tool.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/src/tool.ts b/packages/plugin/src/tool.ts index 7705db3bc9a..0dbc1ed8861 100644 --- a/packages/plugin/src/tool.ts +++ b/packages/plugin/src/tool.ts @@ -2,11 +2,11 @@ import { z } from "zod" import type { FilePart } from "@opencode-ai/sdk" -type Metadata = { +export type Metadata = { [key: string]: any } -type AskInput = { +export type AskInput = { permission: string patterns: string[] always: string[] From da8055834d3adc8ec2e6271197a541630ddbcc5d Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 13 Jan 2026 21:16:30 -0500 Subject: [PATCH 3/5] Prefer const over let --- packages/opencode/src/tool/registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index c5986653bb2..e6fc89f1af7 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -66,7 +66,7 @@ export namespace ToolRegistry { parameters: z.object(def.args), description: def.description, execute: async (args, ctx) => { - let result = await def.execute(args as any, ctx) + const result = await def.execute(args as any, ctx) if (typeof result !== 'string') { return result From 937462776d1dbb881afb96e839eecc545e5eada2 Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 13 Jan 2026 21:18:09 -0500 Subject: [PATCH 4/5] Reorder types to minimize diff --- packages/plugin/src/tool.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/plugin/src/tool.ts b/packages/plugin/src/tool.ts index 0dbc1ed8861..6fc841ab4b7 100644 --- a/packages/plugin/src/tool.ts +++ b/packages/plugin/src/tool.ts @@ -1,18 +1,10 @@ import { z } from "zod" import type { FilePart } from "@opencode-ai/sdk" - export type Metadata = { [key: string]: any } -export type AskInput = { - permission: string - patterns: string[] - always: string[] - metadata: M -} - export type ToolContext = { sessionID: string messageID: string @@ -24,6 +16,13 @@ export type ToolContext = { ask(input: AskInput): Promise } +export type AskInput = { + permission: string + patterns: string[] + always: string[] + metadata: M +} + export type ExecuteResult = { title: string metadata: M From da3af42c2402d4a64f25079374c64ef522172fa6 Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 13 Jan 2026 21:19:30 -0500 Subject: [PATCH 5/5] Remove Input type, it's not really useful by itself --- packages/plugin/src/tool.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/plugin/src/tool.ts b/packages/plugin/src/tool.ts index 6fc841ab4b7..c4c74aacab7 100644 --- a/packages/plugin/src/tool.ts +++ b/packages/plugin/src/tool.ts @@ -30,14 +30,12 @@ export type ExecuteResult = { attachments?: FilePart[] } -export type Input = { +export function tool(input: { description: string args: Args execute(args: z.infer>, context: ToolContext): Promise> formatValidationError?(error: z.ZodError): string -} - -export function tool(input: Input) { +}) { return input } tool.schema = z