@opencode/ai 0.0.0-dev-20120 → 0.0.0-dev-20122

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.
@@ -10,6 +10,7 @@ import { classifyProviderFailure } from "../provider-error.js";
10
10
  import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
11
11
  import { Lifecycle } from "./utils/lifecycle.js";
12
12
  import { MistralToolID } from "./utils/mistral-tool-id.js";
13
+ import { ToolSchemaProjection } from "./utils/tool-schema.js";
13
14
  import { ToolStream } from "./utils/tool-stream.js";
14
15
  const ADAPTER = "mistral-chat";
15
16
  const DONE = "[DONE]";
@@ -272,9 +273,9 @@ const lowerMessages = Effect.fn("MistralChat.lowerMessages")(function* (request)
272
273
  }
273
274
  return messages;
274
275
  });
275
- const lowerTool = (tool) => ({
276
+ const lowerTool = (tool, inputSchema) => ({
276
277
  type: "function",
277
- function: { name: tool.name, description: tool.description, parameters: tool.inputSchema, strict: false },
278
+ function: { name: tool.name, description: tool.description, parameters: inputSchema, strict: false },
278
279
  });
279
280
  export const fromRequest = Effect.fn("MistralChat.fromRequest")(function* (request) {
280
281
  const options = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(MistralOptions))(request.providerOptions ?? {});
@@ -295,7 +296,9 @@ export const fromRequest = Effect.fn("MistralChat.fromRequest")(function* (reque
295
296
  return {
296
297
  model: request.model.id,
297
298
  messages: yield* lowerMessages(flattened.request),
298
- tools: flattened.tools.length > 0 ? flattened.tools.map(lowerTool) : undefined,
299
+ tools: flattened.tools.length > 0
300
+ ? flattened.tools.map((tool) => lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model)))
301
+ : undefined,
299
302
  tool_choice: toolChoice,
300
303
  stream: true,
301
304
  max_tokens: request.generation?.maxTokens,
@@ -54,18 +54,31 @@ const MODEL_NAMES = [
54
54
  [/gemini/i, "gemini"],
55
55
  [/kimi/i, "moonshot"],
56
56
  ];
57
- // An explicit `sanitizer` wins, and `none` opts out. Otherwise the protocol's own default
58
- // applies (the Gemini API always uses Gemini's rules), then the model name selects the family's rules
59
- // so models reached through gateways and OpenAI-compatible endpoints get the same handling.
57
+ // Tool arguments are always a JSON object, and most providers reject a tool schema whose root does not
58
+ // declare `type: "object"`, such as `{}` or a bare `properties` map. Effect encodes an empty struct as
59
+ // `anyOf` object or array; every object matches its bare object branch, so that `anyOf` is dropped.
60
+ const objectRoot = (schema) => {
61
+ if (schema.type !== undefined)
62
+ return schema;
63
+ if (Array.isArray(schema.anyOf) &&
64
+ schema.anyOf.some((branch) => isRecord(branch) && branch.type === "object" && Object.keys(branch).length === 1))
65
+ return { type: "object", ...Object.fromEntries(Object.entries(schema).filter(([key]) => key !== "anyOf")) };
66
+ return { type: "object", ...schema };
67
+ };
68
+ // Every tool schema gets an object root. Then an explicit `sanitizer` wins, and `none` opts out.
69
+ // Otherwise the protocol's own default applies (the Gemini API always uses Gemini's rules), then the
70
+ // model name selects the family's rules so models reached through gateways and OpenAI-compatible
71
+ // endpoints get the same handling.
60
72
  const modelCompatibility = (schema, model, protocolDefault) => {
73
+ const root = objectRoot(schema);
61
74
  switch (model.compatibility?.sanitizer ?? protocolDefault ?? MODEL_NAMES.find(([name]) => name.test(model.id))?.[1]) {
62
75
  case "gemini":
63
- return gemini(schema);
76
+ return gemini(root);
64
77
  case "moonshot":
65
- return moonshot(schema);
78
+ return moonshot(root);
66
79
  case "none":
67
80
  case undefined:
68
- return schema;
81
+ return root;
69
82
  }
70
83
  };
71
84
  export const ToolSchemaProjection = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-20120",
3
+ "version": "0.0.0-dev-20122",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "devDependencies": {
35
35
  "@clack/prompts": "1.0.0-alpha.1",
36
36
  "@effect/platform-node": "4.0.0-rc.112",
37
- "@opencode/http-recorder": "0.0.0-dev-20120",
37
+ "@opencode/http-recorder": "0.0.0-dev-20122",
38
38
  "@tsconfig/bun": "1.0.9",
39
39
  "@types/bun": "1.4.0",
40
40
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -44,7 +44,7 @@
44
44
  "@aws-sdk/credential-providers": "3.1057.0",
45
45
  "@smithy/eventstream-codec": "4.2.14",
46
46
  "@smithy/util-utf8": "4.2.2",
47
- "@opencode/schema": "0.0.0-dev-20120",
47
+ "@opencode/schema": "0.0.0-dev-20122",
48
48
  "aws4fetch": "1.0.20",
49
49
  "effect": "4.0.0-rc.112",
50
50
  "google-auth-library": "10.5.0"