@ai-sdk/harness-opencode 1.0.112 → 1.0.113

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @ai-sdk/harness-opencode
2
2
 
3
+ ## 1.0.113
4
+
5
+ ### Patch Changes
6
+
7
+ - 92c3974: fix(harness-opencode): preserve host tool enum constraints and default metadata
8
+ - @ai-sdk/harness@1.0.111
9
+
3
10
  ## 1.0.112
4
11
 
5
12
  ### Patch Changes
@@ -3,7 +3,66 @@
3
3
  // src/bridge/host-tool-mcp.ts
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+
7
+ // src/bridge/json-schema-to-zod.ts
6
8
  import { z } from "zod/v4";
9
+ function jsonSchemaToZodShape(input) {
10
+ const schema = isJsonSchemaObject(input) ? input : {};
11
+ return toZodShape(schema);
12
+ }
13
+ function toZodShape(schema) {
14
+ if (!schema?.properties) return {};
15
+ const required = new Set(schema.required ?? []);
16
+ const shape = {};
17
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
18
+ const propType = toZodType(propSchema);
19
+ shape[key] = required.has(key) ? propType : propType.optional();
20
+ }
21
+ return shape;
22
+ }
23
+ function toZodType(schema) {
24
+ if (!schema) return z.any();
25
+ let zType = zodForEnum(schema) ?? zodForType(schema);
26
+ if (schema.description) zType = zType.describe(schema.description);
27
+ if (schema.nullable) zType = zType.nullable();
28
+ if ("default" in schema) zType = zType.meta({ default: schema.default });
29
+ return zType;
30
+ }
31
+ function zodForEnum(schema) {
32
+ if (!Array.isArray(schema.enum) || schema.enum.length === 0 || !schema.enum.every(isJsonLiteral)) {
33
+ return void 0;
34
+ }
35
+ return z.literal(schema.enum);
36
+ }
37
+ function zodForType(schema) {
38
+ const types = Array.isArray(schema.type) ? schema.type.filter((t) => t !== "null") : [schema.type].filter(Boolean);
39
+ switch (types[0]) {
40
+ case "string":
41
+ return z.string();
42
+ case "number":
43
+ return z.number();
44
+ case "integer":
45
+ return z.number().int();
46
+ case "boolean":
47
+ return z.boolean();
48
+ case "array":
49
+ return z.array(toZodType(schema.items));
50
+ case "object":
51
+ return z.object(toZodShape(schema));
52
+ case "null":
53
+ return z.null();
54
+ default:
55
+ return z.any();
56
+ }
57
+ }
58
+ function isJsonSchemaObject(input) {
59
+ return input != null && typeof input === "object" && !Array.isArray(input);
60
+ }
61
+ function isJsonLiteral(value) {
62
+ return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
63
+ }
64
+
65
+ // src/bridge/host-tool-mcp.ts
7
66
  var schemas = JSON.parse(process.env.TOOL_SCHEMAS || "[]");
8
67
  var relayUrl = process.env.TOOL_RELAY_URL || "";
9
68
  if (!schemas.length || !relayUrl) {
@@ -14,7 +73,7 @@ if (!schemas.length || !relayUrl) {
14
73
  }
15
74
  var server = new McpServer({ name: "harness-tools", version: "1.0.0" });
16
75
  for (const schema of schemas) {
17
- const shape = toZodShape(schema.inputSchema);
76
+ const shape = jsonSchemaToZodShape(schema.inputSchema);
18
77
  server.tool(
19
78
  schema.name,
20
79
  schema.description ?? "",
@@ -53,49 +112,6 @@ for (const schema of schemas) {
53
112
  }
54
113
  );
55
114
  }
56
- function toZodShape(schema) {
57
- if (!schema?.properties) return {};
58
- const required = new Set(schema.required ?? []);
59
- const shape = {};
60
- for (const [key, propSchema] of Object.entries(schema.properties)) {
61
- const propType = toZodType(propSchema);
62
- shape[key] = required.has(key) ? propType : propType.optional();
63
- }
64
- return shape;
65
- }
66
- function toZodType(schema) {
67
- if (!schema) return z.any();
68
- const types = Array.isArray(schema.type) ? schema.type.filter((t) => t !== "null") : [schema.type].filter(Boolean);
69
- let zType;
70
- switch (types[0]) {
71
- case "string":
72
- zType = z.string();
73
- break;
74
- case "number":
75
- zType = z.number();
76
- break;
77
- case "integer":
78
- zType = z.number().int();
79
- break;
80
- case "boolean":
81
- zType = z.boolean();
82
- break;
83
- case "array":
84
- zType = z.array(toZodType(schema.items));
85
- break;
86
- case "object":
87
- zType = z.object(toZodShape(schema));
88
- break;
89
- case "null":
90
- zType = z.null();
91
- break;
92
- default:
93
- zType = z.any();
94
- }
95
- if (schema.description) zType = zType.describe(schema.description);
96
- if (schema.nullable) zType = zType.nullable();
97
- return zType;
98
- }
99
115
  var transport = new StdioServerTransport();
100
116
  await server.connect(transport);
101
117
  //# sourceMappingURL=host-tool-mcp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bridge/host-tool-mcp.ts"],"sourcesContent":["#!/usr/bin/env node\n/*\n * These bridge imports are externalized by tsup and resolved inside the\n * sandbox from src/bridge/package.json and its lockfile. Keep this file,\n * tsup.config.ts, and the bridge package dependency list in sync.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod/v4';\n\ntype ToolSchema = {\n name: string;\n description?: string;\n inputSchema?: JsonSchemaObject;\n};\n\ntype JsonSchemaObject = {\n type?: string | string[];\n description?: string;\n properties?: Record<string, JsonSchemaObject>;\n required?: string[];\n items?: JsonSchemaObject;\n enum?: unknown[];\n const?: unknown;\n oneOf?: JsonSchemaObject[];\n anyOf?: JsonSchemaObject[];\n additionalProperties?: boolean | JsonSchemaObject;\n nullable?: boolean;\n};\n\ntype ZodShape = Record<string, z.ZodTypeAny>;\n\nconst schemas: ToolSchema[] = JSON.parse(process.env.TOOL_SCHEMAS || '[]');\nconst relayUrl = process.env.TOOL_RELAY_URL || '';\n\nif (!schemas.length || !relayUrl) {\n process.stderr.write(\n '[host-tool-mcp] Missing TOOL_SCHEMAS or TOOL_RELAY_URL; exiting\\n',\n );\n process.exit(0);\n}\n\nconst server = new McpServer({ name: 'harness-tools', version: '1.0.0' });\n\nfor (const schema of schemas) {\n const shape = toZodShape(schema.inputSchema);\n server.tool(\n schema.name,\n schema.description ?? '',\n shape,\n async (input: Record<string, unknown>) => {\n const requestId = crypto.randomUUID();\n try {\n const res = await fetch(relayUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ requestId, toolName: schema.name, input }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(\n `Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}`,\n );\n }\n const data = (await res.json()) as { result?: unknown };\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(data.result ?? null),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Error: ${String(err)}` }],\n isError: true,\n };\n }\n },\n );\n}\n\nfunction toZodShape(schema: JsonSchemaObject | undefined): ZodShape {\n if (!schema?.properties) return {};\n const required = new Set(schema.required ?? []);\n const shape: ZodShape = {};\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const propType = toZodType(propSchema);\n shape[key] = required.has(key) ? propType : propType.optional();\n }\n return shape;\n}\n\nfunction toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {\n if (!schema) return z.any();\n const types = Array.isArray(schema.type)\n ? schema.type.filter((t): t is string => t !== 'null')\n : ([schema.type].filter(Boolean) as string[]);\n let zType: z.ZodTypeAny;\n switch (types[0]) {\n case 'string':\n zType = z.string();\n break;\n case 'number':\n zType = z.number();\n break;\n case 'integer':\n zType = z.number().int();\n break;\n case 'boolean':\n zType = z.boolean();\n break;\n case 'array':\n zType = z.array(toZodType(schema.items));\n break;\n case 'object':\n zType = z.object(toZodShape(schema));\n break;\n case 'null':\n zType = z.null();\n break;\n default:\n zType = z.any();\n }\n if (schema.description) zType = zType.describe(schema.description);\n if (schema.nullable) zType = zType.nullable();\n return zType;\n}\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/bridge/host-tool-mcp.ts","../../src/bridge/json-schema-to-zod.ts"],"sourcesContent":["#!/usr/bin/env node\n/*\n * These bridge imports are externalized by tsup and resolved inside the\n * sandbox from src/bridge/package.json and its lockfile. Keep this file,\n * tsup.config.ts, and the bridge package dependency list in sync.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { jsonSchemaToZodShape } from './json-schema-to-zod';\n\ntype ToolSchema = {\n name: string;\n description?: string;\n inputSchema?: unknown;\n};\n\nconst schemas: ToolSchema[] = JSON.parse(process.env.TOOL_SCHEMAS || '[]');\nconst relayUrl = process.env.TOOL_RELAY_URL || '';\n\nif (!schemas.length || !relayUrl) {\n process.stderr.write(\n '[host-tool-mcp] Missing TOOL_SCHEMAS or TOOL_RELAY_URL; exiting\\n',\n );\n process.exit(0);\n}\n\nconst server = new McpServer({ name: 'harness-tools', version: '1.0.0' });\n\nfor (const schema of schemas) {\n const shape = jsonSchemaToZodShape(schema.inputSchema);\n server.tool(\n schema.name,\n schema.description ?? '',\n shape,\n async (input: Record<string, unknown>) => {\n const requestId = crypto.randomUUID();\n try {\n const res = await fetch(relayUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ requestId, toolName: schema.name, input }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(\n `Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}`,\n );\n }\n const data = (await res.json()) as { result?: unknown };\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(data.result ?? null),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Error: ${String(err)}` }],\n isError: true,\n };\n }\n },\n );\n}\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { z } from 'zod/v4';\n\ntype JsonLiteral = string | number | boolean | null;\n\ntype JsonSchemaObject = {\n type?: string | string[];\n description?: string;\n properties?: Record<string, JsonSchemaObject>;\n required?: string[];\n items?: JsonSchemaObject;\n enum?: unknown[];\n default?: unknown;\n nullable?: boolean;\n};\n\ntype ZodShape = Record<string, z.ZodTypeAny>;\n\nexport function jsonSchemaToZodShape(input: unknown): ZodShape {\n const schema = isJsonSchemaObject(input) ? input : {};\n return toZodShape(schema);\n}\n\nfunction toZodShape(schema: JsonSchemaObject | undefined): ZodShape {\n if (!schema?.properties) return {};\n const required = new Set(schema.required ?? []);\n const shape: ZodShape = {};\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const propType = toZodType(propSchema);\n shape[key] = required.has(key) ? propType : propType.optional();\n }\n return shape;\n}\n\nfunction toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {\n if (!schema) return z.any();\n\n let zType = zodForEnum(schema) ?? zodForType(schema);\n if (schema.description) zType = zType.describe(schema.description);\n if (schema.nullable) zType = zType.nullable();\n if ('default' in schema) zType = zType.meta({ default: schema.default });\n return zType;\n}\n\nfunction zodForEnum(schema: JsonSchemaObject): z.ZodTypeAny | undefined {\n if (\n !Array.isArray(schema.enum) ||\n schema.enum.length === 0 ||\n !schema.enum.every(isJsonLiteral)\n ) {\n return undefined;\n }\n return z.literal(schema.enum);\n}\n\nfunction zodForType(schema: JsonSchemaObject): z.ZodTypeAny {\n const types = Array.isArray(schema.type)\n ? schema.type.filter((t): t is string => t !== 'null')\n : ([schema.type].filter(Boolean) as string[]);\n\n switch (types[0]) {\n case 'string':\n return z.string();\n case 'number':\n return z.number();\n case 'integer':\n return z.number().int();\n case 'boolean':\n return z.boolean();\n case 'array':\n return z.array(toZodType(schema.items));\n case 'object':\n return z.object(toZodShape(schema));\n case 'null':\n return z.null();\n default:\n return z.any();\n }\n}\n\nfunction isJsonSchemaObject(input: unknown): input is JsonSchemaObject {\n return input != null && typeof input === 'object' && !Array.isArray(input);\n}\n\nfunction isJsonLiteral(value: unknown): value is JsonLiteral {\n return (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean' ||\n (typeof value === 'number' && Number.isFinite(value))\n );\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -909,7 +909,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
909
909
  ]);
910
910
 
911
911
  // src/version.ts
912
- var VERSION = true ? "1.0.112" : "0.0.0-test";
912
+ var VERSION = true ? "1.0.113" : "0.0.0-test";
913
913
 
914
914
  // src/opencode-harness.ts
915
915
  var OPENCODE_CLIENT_APP = `ai-sdk/harness-opencode/${VERSION}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness-opencode",
3
- "version": "1.0.112",
3
+ "version": "1.0.113",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -26,7 +26,7 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@ai-sdk/harness": "1.0.110",
29
+ "@ai-sdk/harness": "1.0.111",
30
30
  "@ai-sdk/provider-utils": "5.0.40",
31
31
  "ws": "8.21.0"
32
32
  },
@@ -6,30 +6,14 @@
6
6
  */
7
7
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
8
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
- import { z } from 'zod/v4';
9
+ import { jsonSchemaToZodShape } from './json-schema-to-zod';
10
10
 
11
11
  type ToolSchema = {
12
12
  name: string;
13
13
  description?: string;
14
- inputSchema?: JsonSchemaObject;
14
+ inputSchema?: unknown;
15
15
  };
16
16
 
17
- type JsonSchemaObject = {
18
- type?: string | string[];
19
- description?: string;
20
- properties?: Record<string, JsonSchemaObject>;
21
- required?: string[];
22
- items?: JsonSchemaObject;
23
- enum?: unknown[];
24
- const?: unknown;
25
- oneOf?: JsonSchemaObject[];
26
- anyOf?: JsonSchemaObject[];
27
- additionalProperties?: boolean | JsonSchemaObject;
28
- nullable?: boolean;
29
- };
30
-
31
- type ZodShape = Record<string, z.ZodTypeAny>;
32
-
33
17
  const schemas: ToolSchema[] = JSON.parse(process.env.TOOL_SCHEMAS || '[]');
34
18
  const relayUrl = process.env.TOOL_RELAY_URL || '';
35
19
 
@@ -43,7 +27,7 @@ if (!schemas.length || !relayUrl) {
43
27
  const server = new McpServer({ name: 'harness-tools', version: '1.0.0' });
44
28
 
45
29
  for (const schema of schemas) {
46
- const shape = toZodShape(schema.inputSchema);
30
+ const shape = jsonSchemaToZodShape(schema.inputSchema);
47
31
  server.tool(
48
32
  schema.name,
49
33
  schema.description ?? '',
@@ -83,52 +67,5 @@ for (const schema of schemas) {
83
67
  );
84
68
  }
85
69
 
86
- function toZodShape(schema: JsonSchemaObject | undefined): ZodShape {
87
- if (!schema?.properties) return {};
88
- const required = new Set(schema.required ?? []);
89
- const shape: ZodShape = {};
90
- for (const [key, propSchema] of Object.entries(schema.properties)) {
91
- const propType = toZodType(propSchema);
92
- shape[key] = required.has(key) ? propType : propType.optional();
93
- }
94
- return shape;
95
- }
96
-
97
- function toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {
98
- if (!schema) return z.any();
99
- const types = Array.isArray(schema.type)
100
- ? schema.type.filter((t): t is string => t !== 'null')
101
- : ([schema.type].filter(Boolean) as string[]);
102
- let zType: z.ZodTypeAny;
103
- switch (types[0]) {
104
- case 'string':
105
- zType = z.string();
106
- break;
107
- case 'number':
108
- zType = z.number();
109
- break;
110
- case 'integer':
111
- zType = z.number().int();
112
- break;
113
- case 'boolean':
114
- zType = z.boolean();
115
- break;
116
- case 'array':
117
- zType = z.array(toZodType(schema.items));
118
- break;
119
- case 'object':
120
- zType = z.object(toZodShape(schema));
121
- break;
122
- case 'null':
123
- zType = z.null();
124
- break;
125
- default:
126
- zType = z.any();
127
- }
128
- if (schema.description) zType = zType.describe(schema.description);
129
- if (schema.nullable) zType = zType.nullable();
130
- return zType;
131
- }
132
-
133
70
  const transport = new StdioServerTransport();
134
71
  await server.connect(transport);
@@ -0,0 +1,91 @@
1
+ import { z } from 'zod/v4';
2
+
3
+ type JsonLiteral = string | number | boolean | null;
4
+
5
+ type JsonSchemaObject = {
6
+ type?: string | string[];
7
+ description?: string;
8
+ properties?: Record<string, JsonSchemaObject>;
9
+ required?: string[];
10
+ items?: JsonSchemaObject;
11
+ enum?: unknown[];
12
+ default?: unknown;
13
+ nullable?: boolean;
14
+ };
15
+
16
+ type ZodShape = Record<string, z.ZodTypeAny>;
17
+
18
+ export function jsonSchemaToZodShape(input: unknown): ZodShape {
19
+ const schema = isJsonSchemaObject(input) ? input : {};
20
+ return toZodShape(schema);
21
+ }
22
+
23
+ function toZodShape(schema: JsonSchemaObject | undefined): ZodShape {
24
+ if (!schema?.properties) return {};
25
+ const required = new Set(schema.required ?? []);
26
+ const shape: ZodShape = {};
27
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
28
+ const propType = toZodType(propSchema);
29
+ shape[key] = required.has(key) ? propType : propType.optional();
30
+ }
31
+ return shape;
32
+ }
33
+
34
+ function toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {
35
+ if (!schema) return z.any();
36
+
37
+ let zType = zodForEnum(schema) ?? zodForType(schema);
38
+ if (schema.description) zType = zType.describe(schema.description);
39
+ if (schema.nullable) zType = zType.nullable();
40
+ if ('default' in schema) zType = zType.meta({ default: schema.default });
41
+ return zType;
42
+ }
43
+
44
+ function zodForEnum(schema: JsonSchemaObject): z.ZodTypeAny | undefined {
45
+ if (
46
+ !Array.isArray(schema.enum) ||
47
+ schema.enum.length === 0 ||
48
+ !schema.enum.every(isJsonLiteral)
49
+ ) {
50
+ return undefined;
51
+ }
52
+ return z.literal(schema.enum);
53
+ }
54
+
55
+ function zodForType(schema: JsonSchemaObject): z.ZodTypeAny {
56
+ const types = Array.isArray(schema.type)
57
+ ? schema.type.filter((t): t is string => t !== 'null')
58
+ : ([schema.type].filter(Boolean) as string[]);
59
+
60
+ switch (types[0]) {
61
+ case 'string':
62
+ return z.string();
63
+ case 'number':
64
+ return z.number();
65
+ case 'integer':
66
+ return z.number().int();
67
+ case 'boolean':
68
+ return z.boolean();
69
+ case 'array':
70
+ return z.array(toZodType(schema.items));
71
+ case 'object':
72
+ return z.object(toZodShape(schema));
73
+ case 'null':
74
+ return z.null();
75
+ default:
76
+ return z.any();
77
+ }
78
+ }
79
+
80
+ function isJsonSchemaObject(input: unknown): input is JsonSchemaObject {
81
+ return input != null && typeof input === 'object' && !Array.isArray(input);
82
+ }
83
+
84
+ function isJsonLiteral(value: unknown): value is JsonLiteral {
85
+ return (
86
+ value === null ||
87
+ typeof value === 'string' ||
88
+ typeof value === 'boolean' ||
89
+ (typeof value === 'number' && Number.isFinite(value))
90
+ );
91
+ }