@opencode-ai/plugin 0.0.0-next-15632 → 0.0.0-next-15634

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { Agent } from "@opencode-ai/schema/agent";
3
3
  import type { LLM } from "@opencode-ai/schema/llm";
4
4
  import { Session } from "@opencode-ai/schema/session";
5
5
  import { SessionMessage } from "@opencode-ai/schema/session-message";
6
+ import type { StandardJSONSchemaV1, StandardSchemaV1 } from "@standard-schema/spec";
6
7
  import { Effect, JsonSchema, Schema } from "effect";
7
8
  import type { Hooks, Transform } from "./registration.js";
8
9
  export interface Context {
@@ -16,7 +17,12 @@ export interface Progress {
16
17
  readonly structured: Readonly<Record<string, unknown>>;
17
18
  readonly content?: ReadonlyArray<Content>;
18
19
  }
19
- export type SchemaType<A> = Schema.Codec<A, any>;
20
+ export type StandardSchemaType<Input = unknown, Output = Input> = StandardSchemaV1<Input, Output> & StandardJSONSchemaV1<Input, Output>;
21
+ export type SchemaType<A> = Schema.Codec<A, any> | StandardSchemaType<any, A>;
22
+ type IsAny<A> = 0 extends 1 & A ? true : false;
23
+ export type InputValue<S> = IsAny<S> extends true ? any : S extends Schema.Codec<infer A, any> ? A : S extends StandardSchemaV1<any, infer A> ? A : never;
24
+ export type OutputValue<S> = IsAny<S> extends true ? any : S extends Schema.Codec<infer A, any> ? A : S extends StandardSchemaV1<infer A, any> ? A : never;
25
+ export type EncodedValue<S> = IsAny<S> extends true ? any : S extends Schema.Codec<any, infer A> ? A : S extends StandardSchemaV1<any, infer A> ? A : never;
20
26
  type ToolDefinition = {
21
27
  readonly name: string;
22
28
  readonly description: string;
@@ -44,14 +50,6 @@ type ToolOutput = {
44
50
  readonly structured: unknown;
45
51
  readonly content: ReadonlyArray<LLM.ToolContent>;
46
52
  };
47
- declare const TypeId: unique symbol;
48
- export interface Definition<Input extends SchemaType<any>, Output extends SchemaType<any>> {
49
- readonly [TypeId]: {
50
- readonly _Input: Input;
51
- readonly _Output: Output;
52
- };
53
- }
54
- export type AnyTool = Definition<any, any>;
55
53
  declare const Failure_base: Schema.Class<Failure, Schema.TaggedStruct<"LLM.ToolFailure", {
56
54
  readonly message: Schema.String;
57
55
  readonly error: Schema.optional<Schema.Defect>;
@@ -74,19 +72,20 @@ export type Content = {
74
72
  readonly mime: string;
75
73
  readonly name?: string;
76
74
  };
77
- type Config<Input extends SchemaType<any>, Output extends SchemaType<any>, Structured extends SchemaType<any> = Output> = {
75
+ export type Definition<Input extends SchemaType<any>, Structured extends SchemaType<any>, Output extends SchemaType<any> = any> = {
78
76
  readonly description: string;
79
77
  readonly input: Input;
80
78
  readonly output: Output;
81
79
  readonly structured?: Structured;
80
+ readonly permission?: string;
82
81
  readonly toStructuredOutput?: (input: {
83
- readonly input: Schema.Schema.Type<Input>;
84
- readonly output: Output["Encoded"];
85
- }) => Schema.Schema.Type<Structured>;
86
- readonly execute: (input: Schema.Schema.Type<Input>, context: Context) => Effect.Effect<Schema.Schema.Type<Output>, Failure>;
82
+ readonly input: InputValue<Input>;
83
+ readonly output: EncodedValue<Output>;
84
+ }) => OutputValue<Structured>;
85
+ readonly execute: (input: InputValue<Input>, context: Context) => Effect.Effect<OutputValue<Output>, Failure>;
87
86
  readonly toModelOutput?: (input: {
88
- readonly input: Schema.Schema.Type<Input>;
89
- readonly output: Output["Encoded"];
87
+ readonly input: InputValue<Input>;
88
+ readonly output: EncodedValue<Output>;
90
89
  }) => ReadonlyArray<Content>;
91
90
  };
92
91
  export type DynamicOutput = {
@@ -98,14 +97,17 @@ export type DynamicOutput = {
98
97
  * time (MCP servers, plugin manifests). Input is passed through as `unknown`;
99
98
  * `execute` returns the already-projected structured value and model content.
100
99
  */
101
- type DynamicConfig = {
100
+ export type DynamicDefinition = {
102
101
  readonly description: string;
103
102
  readonly jsonSchema: JsonSchema.JsonSchema;
104
103
  readonly outputSchema?: JsonSchema.JsonSchema;
104
+ readonly permission?: string;
105
105
  readonly execute: (input: unknown, context: Context) => Effect.Effect<DynamicOutput, Failure>;
106
106
  };
107
- export declare function make<Input extends SchemaType<any>, Output extends SchemaType<any>, Structured extends SchemaType<any> = Output>(config: Config<Input, Output, Structured>): Definition<Input, Structured>;
108
- export declare function make(config: DynamicConfig): AnyTool;
107
+ export type AnyTool = Definition<any, any> | DynamicDefinition;
108
+ export declare function make<Input extends SchemaType<any>, Output extends SchemaType<any>, Structured extends SchemaType<any> = Output>(config: Definition<Input, Structured, Output>): Definition<Input, Structured, Output>;
109
+ export declare function make(config: DynamicDefinition): DynamicDefinition;
110
+ export declare function make(config: AnyTool): AnyTool;
109
111
  export declare const validateName: (name: string) => Effect.Effect<void, never, never> | Effect.Effect<never, RegistrationError, never>;
110
112
  export declare const registrationEntries: (tools: Readonly<Record<string, AnyTool>>, group?: string) => {
111
113
  key: string;
@@ -113,10 +115,12 @@ export declare const registrationEntries: (tools: Readonly<Record<string, AnyToo
113
115
  group: string | undefined;
114
116
  tool: AnyTool;
115
117
  }[];
116
- export declare const withPermission: <Input extends SchemaType<any>, Output extends SchemaType<any>>(tool: Definition<Input, Output>, permission: string) => Definition<Input, Output>;
118
+ export declare const withPermission: <T extends AnyTool>(tool: T, permission: string) => Omit<T, "permission"> & {
119
+ readonly permission: string;
120
+ };
117
121
  export declare const permission: (tool: AnyTool, name: string) => string;
118
122
  export declare const definition: (name: string, tool: AnyTool) => ToolDefinition;
119
- export declare const settle: (tool: AnyTool, call: ToolCall, context: Context) => Effect.Effect<ToolOutput, Failure, never>;
123
+ export declare const settle: (tool: AnyTool, call: ToolCall, context: Context) => Effect.Effect<ToolOutput, Failure>;
120
124
  export interface ToolExecuteBeforeEvent {
121
125
  readonly tool: string;
122
126
  readonly sessionID: Session.ID;
@@ -14,65 +14,8 @@ export class RegistrationError extends Schema.TaggedErrorClass()("Tool.Registrat
14
14
  message: Schema.String,
15
15
  }) {
16
16
  }
17
- const runtimes = new WeakMap();
18
17
  export function make(config) {
19
- if ("jsonSchema" in config)
20
- return makeDynamic(config);
21
- return makeTyped(config);
22
- }
23
- function makeTyped(config) {
24
- const tool = Object.freeze({});
25
- const definitions = new Map();
26
- runtimes.set(tool, {
27
- definition: (name) => {
28
- const cached = definitions.get(name);
29
- if (cached)
30
- return cached;
31
- const definition = {
32
- name,
33
- description: config.description,
34
- inputSchema: toJsonSchema(config.input),
35
- outputSchema: toJsonSchema(config.structured ?? config.output),
36
- };
37
- definitions.set(name, definition);
38
- return definition;
39
- },
40
- settle: (call, context) => Schema.decodeUnknownEffect(config.input)(call.input).pipe(Effect.mapError((error) => new Failure({ message: `Invalid tool input: ${error.message}` })), Effect.flatMap((input) => config.execute(input, context).pipe(Effect.flatMap((output) => Schema.encodeEffect(config.output)(output).pipe(Effect.flatMap((output) => {
41
- if (!config.structured || !config.toStructuredOutput)
42
- return Effect.succeed({ output, structured: output });
43
- return Schema.encodeEffect(config.structured)(config.toStructuredOutput({ input, output })).pipe(Effect.map((structured) => ({ output, structured })));
44
- }), Effect.mapError((error) => new Failure({
45
- message: `Tool returned an invalid value for its output schema: ${error.message}`,
46
- })))), Effect.map(({ output, structured }) => ({
47
- structured,
48
- content: config.toModelOutput?.({ input, output }).map(toModelContent) ??
49
- (typeof output === "string" ? [{ type: "text", text: output }] : []),
50
- }))))),
51
- });
52
- return tool;
53
- }
54
- function makeDynamic(config) {
55
- const tool = Object.freeze({});
56
- const definitions = new Map();
57
- runtimes.set(tool, {
58
- definition: (name) => {
59
- const cached = definitions.get(name);
60
- if (cached)
61
- return cached;
62
- const definition = {
63
- name,
64
- description: config.description,
65
- inputSchema: config.jsonSchema,
66
- outputSchema: config.outputSchema,
67
- };
68
- definitions.set(name, definition);
69
- return definition;
70
- },
71
- settle: (call, context) => config
72
- .execute(call.input, context)
73
- .pipe(Effect.map((output) => ({ structured: output.structured, content: output.content.map(toModelContent) }))),
74
- });
75
- return tool;
18
+ return config;
76
19
  }
77
20
  function toModelContent(part) {
78
21
  if (part.type === "text")
@@ -92,19 +35,74 @@ export const registrationEntries = (tools, group) => Object.entries(tools).map((
92
35
  tool,
93
36
  };
94
37
  });
95
- export const withPermission = (tool, permission) => {
96
- const decorated = Object.freeze({});
97
- runtimes.set(decorated, { ...runtimeOf(tool), permission });
98
- return decorated;
99
- };
100
- export const permission = (tool, name) => runtimeOf(tool).permission ?? name;
101
- export const definition = (name, tool) => runtimeOf(tool).definition(name);
102
- export const settle = (tool, call, context) => runtimeOf(tool).settle(call, context);
103
- function runtimeOf(tool) {
104
- const runtime = runtimes.get(tool);
105
- if (!runtime)
106
- throw new TypeError("Invalid Tool value");
107
- return runtime;
38
+ export const withPermission = (tool, permission) => ({ ...tool, permission });
39
+ export const permission = (tool, name) => tool.permission ?? name;
40
+ export const definition = (name, tool) => "jsonSchema" in tool
41
+ ? {
42
+ name,
43
+ description: tool.description,
44
+ inputSchema: tool.jsonSchema,
45
+ outputSchema: tool.outputSchema,
46
+ }
47
+ : {
48
+ name,
49
+ description: tool.description,
50
+ inputSchema: inputJsonSchema(tool.input),
51
+ outputSchema: outputJsonSchema(tool.structured ?? tool.output),
52
+ };
53
+ export const settle = (tool, call, context) => Effect.gen(function* () {
54
+ if ("jsonSchema" in tool) {
55
+ const output = yield* tool.execute(call.input, context);
56
+ return { structured: output.structured, content: output.content.map(toModelContent) };
57
+ }
58
+ const input = yield* decodeInput(tool.input, call.input);
59
+ const value = yield* tool.execute(input, context);
60
+ const output = yield* encodeOutput(tool.output, value);
61
+ const structured = tool.structured && tool.toStructuredOutput
62
+ ? yield* encodeOutput(tool.structured, tool.toStructuredOutput({ input, output }))
63
+ : output;
64
+ return {
65
+ structured,
66
+ content: tool.toModelOutput?.({ input, output }).map(toModelContent) ??
67
+ (typeof output === "string" ? [{ type: "text", text: output }] : []),
68
+ };
69
+ });
70
+ function decodeInput(schema, value) {
71
+ if (Schema.isSchema(schema))
72
+ return Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError((error) => new Failure({ message: `Invalid tool input: ${error.message}` })));
73
+ return validateStandard(schema, value, "Invalid tool input");
74
+ }
75
+ function encodeOutput(schema, value) {
76
+ if (Schema.isSchema(schema))
77
+ return Schema.encodeEffect(schema)(value).pipe(Effect.mapError((error) => new Failure({ message: `Tool returned an invalid value for its output schema: ${error.message}` })));
78
+ return validateStandard(schema, value, "Tool returned an invalid value for its output schema");
79
+ }
80
+ function validateStandard(schema, value, prefix) {
81
+ return Effect.gen(function* () {
82
+ const pending = yield* Effect.try({
83
+ try: () => schema["~standard"].validate(value),
84
+ catch: (error) => standardFailure(prefix, error),
85
+ });
86
+ const result = pending instanceof Promise
87
+ ? yield* Effect.tryPromise({ try: () => pending, catch: (error) => standardFailure(prefix, error) })
88
+ : pending;
89
+ if (result.issues)
90
+ return yield* Effect.fail(new Failure({ message: `${prefix}: ${result.issues.map((issue) => issue.message).join(", ")}` }));
91
+ return result.value;
92
+ });
93
+ }
94
+ function standardFailure(prefix, error) {
95
+ return new Failure({ message: `${prefix}: ${error instanceof Error ? error.message : String(error)}` });
96
+ }
97
+ function inputJsonSchema(schema) {
98
+ if (!Schema.isSchema(schema))
99
+ return schema["~standard"].jsonSchema.input({ target: "draft-2020-12" });
100
+ return toJsonSchema(schema);
101
+ }
102
+ function outputJsonSchema(schema) {
103
+ if (!Schema.isSchema(schema))
104
+ return schema["~standard"].jsonSchema.output({ target: "draft-2020-12" });
105
+ return toJsonSchema(schema);
108
106
  }
109
107
  function toJsonSchema(schema) {
110
108
  const document = Schema.toJsonSchemaDocument(schema);
@@ -1,8 +1,4 @@
1
1
  import type { Tool } from "../effect/tool.js";
2
- import type { Agent } from "@opencode-ai/schema/agent";
3
- import type { Session } from "@opencode-ai/schema/session";
4
- import type { SessionMessage } from "@opencode-ai/schema/session-message";
5
- import type { JsonSchema, Schema } from "effect";
6
2
  import type { Hooks, Transform } from "./registration.js";
7
3
  export type Context = Omit<Tool.Context, "progress"> & {
8
4
  readonly progress: (update: Tool.Progress) => Promise<void>;
@@ -10,56 +6,20 @@ export type Context = Omit<Tool.Context, "progress"> & {
10
6
  export type SchemaType<A> = Tool.SchemaType<A>;
11
7
  export type Content = Tool.Content;
12
8
  export type DynamicOutput = Tool.DynamicOutput;
13
- export type Definition<Input extends SchemaType<any>, Output extends SchemaType<any>, Structured extends SchemaType<any> = Output> = {
9
+ export type Definition<Input extends SchemaType<any>, Output extends SchemaType<any>, Structured extends SchemaType<any> = Output> = Omit<Tool.Definition<Input, Structured, Output>, "execute" | "permission"> & {
14
10
  readonly name: string;
15
11
  readonly options?: RegisterOptions;
16
- readonly description: string;
17
- readonly input: Input;
18
- readonly output: Output;
19
- readonly structured?: Structured;
20
- readonly toStructuredOutput?: (input: {
21
- readonly input: Schema.Schema.Type<Input>;
22
- readonly output: Output["Encoded"];
23
- }) => Schema.Schema.Type<Structured>;
24
- readonly execute: (input: Schema.Schema.Type<Input>, context: Context) => Promise<Schema.Schema.Type<Output>>;
25
- readonly toModelOutput?: (input: {
26
- readonly input: Schema.Schema.Type<Input>;
27
- readonly output: Output["Encoded"];
28
- }) => ReadonlyArray<Content>;
12
+ readonly execute: (input: Tool.InputValue<Input>, context: Context) => Promise<Tool.OutputValue<Output>>;
29
13
  };
30
- export type DynamicDefinition = {
14
+ export type DynamicDefinition = Omit<Tool.DynamicDefinition, "execute" | "permission"> & {
31
15
  readonly name: string;
32
16
  readonly options?: RegisterOptions;
33
- readonly description: string;
34
- readonly jsonSchema: JsonSchema.JsonSchema;
35
- readonly outputSchema?: JsonSchema.JsonSchema;
36
17
  readonly execute: (input: unknown, context: Context) => Promise<DynamicOutput>;
37
18
  };
38
19
  export type AnyTool = Definition<any, any, any> | DynamicDefinition;
39
- export interface ToolExecuteBeforeEvent {
40
- readonly tool: string;
41
- readonly sessionID: Session.ID;
42
- readonly agent: Agent.ID;
43
- readonly messageID: SessionMessage.ID;
44
- readonly callID: string;
45
- input: unknown;
46
- }
47
- export interface ToolExecuteAfterEvent {
48
- readonly tool: string;
49
- readonly sessionID: Session.ID;
50
- readonly agent: Agent.ID;
51
- readonly messageID: SessionMessage.ID;
52
- readonly callID: string;
53
- readonly input: unknown;
54
- result: Tool.ToolExecuteAfterEvent["result"];
55
- output?: Tool.ToolExecuteAfterEvent["output"];
56
- outputPaths?: ReadonlyArray<string>;
57
- }
58
- export interface RegisterOptions {
59
- readonly group?: string;
60
- /** Defaults to true. False exposes the tool directly to the provider. */
61
- readonly codemode?: boolean;
62
- }
20
+ export type ToolExecuteBeforeEvent = Tool.ToolExecuteBeforeEvent;
21
+ export type ToolExecuteAfterEvent = Tool.ToolExecuteAfterEvent;
22
+ export type RegisterOptions = Tool.RegisterOptions;
63
23
  export interface ToolDraft {
64
24
  add<Input extends SchemaType<any>, Output extends SchemaType<any>, Structured extends SchemaType<any> = Output>(tool: Definition<Input, Output, Structured>): void;
65
25
  add(tool: DynamicDefinition): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode-ai/plugin",
4
- "version": "0.0.0-next-15632",
4
+ "version": "0.0.0-next-15634",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -52,10 +52,11 @@
52
52
  ],
53
53
  "dependencies": {
54
54
  "@ai-sdk/provider": "3.0.8",
55
- "@opencode-ai/ai": "0.0.0-next-15632",
56
- "@opencode-ai/client": "0.0.0-next-15632",
57
- "@opencode-ai/schema": "0.0.0-next-15632",
58
- "@opencode-ai/sdk": "0.0.0-next-15632",
55
+ "@opencode-ai/ai": "0.0.0-next-15634",
56
+ "@opencode-ai/client": "0.0.0-next-15634",
57
+ "@opencode-ai/schema": "0.0.0-next-15634",
58
+ "@opencode-ai/sdk": "0.0.0-next-15634",
59
+ "@standard-schema/spec": "^1.1.0",
59
60
  "effect": "4.0.0-beta.83",
60
61
  "zod": "4.1.8"
61
62
  },