diff --git a/bun.lock b/bun.lock index 16521122..e6da18ca 100644 --- a/bun.lock +++ b/bun.lock @@ -86,6 +86,9 @@ "sharp", "esbuild", ], + "patchedDependencies": { + "ai@4.3.16": "patches/ai@4.3.16.patch", + }, "overrides": { "zod": "3.24.2", }, diff --git a/package.json b/package.json index f454d7d9..d3968d5d 100644 --- a/package.json +++ b/package.json @@ -37,5 +37,8 @@ "esbuild", "protobufjs", "sharp" - ] + ], + "patchedDependencies": { + "ai@4.3.16": "patches/ai@4.3.16.patch" + } } diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 287d6270..8bb0055a 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -93,8 +93,11 @@ const cli = yargs(hideBin(process.argv)) if (Installation.VERSION === latest) return const method = await Installation.method() if (method === "unknown") return - await Installation.upgrade(method, latest).catch(() => {}) - Bus.publish(Installation.Event.Updated, { version: latest }) + await Installation.upgrade(method, latest) + .then(() => { + Bus.publish(Installation.Event.Updated, { version: latest }) + }) + .catch(() => {}) })() await proc.exited diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts index 54d50cc3..e7528bcd 100644 --- a/packages/opencode/src/installation/index.ts +++ b/packages/opencode/src/installation/index.ts @@ -117,6 +117,6 @@ export namespace Installation { export async function latest() { return fetch("https://api.github.com/repos/sst/opencode/releases/latest") .then((res) => res.json()) - .then((data) => data.tag_name.slice(1)) + .then((data) => data.tag_name.slice(1) as string) } } diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 9a08c4e3..84d5e31d 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -23,6 +23,7 @@ import { ModelsDev } from "./models" import { NamedError } from "../util/error" import { Auth } from "../auth" import { TaskTool } from "../tool/task" +import { GlobalConfig } from "../global/config" export namespace Provider { const log = Log.create({ service: "provider" }) @@ -257,7 +258,10 @@ export namespace Provider { } export async function defaultModel() { - const [provider] = await list().then((val) => Object.values(val)) + const cfg = await GlobalConfig.get() + const provider = await list() + .then((val) => Object.values(val)) + .then((x) => x.find((p) => !cfg.provider || cfg.provider === p.info.id)) if (!provider) throw new Error("no providers found") const [model] = sort(Object.values(provider.info.models)) if (!model) throw new Error("no models found") @@ -285,11 +289,16 @@ export namespace Provider { TaskTool, TodoReadTool, ] + const TOOL_MAPPING: Record = { anthropic: TOOLS.filter((t) => t.id !== "opencode.patch"), - openai: TOOLS, + openai: TOOLS.map((t) => ({ + ...t, + parameters: optionalToNullable(t.parameters), + })), google: TOOLS, } + export async function tools(providerID: string) { /* const cfg = await Config.get() @@ -301,6 +310,38 @@ export namespace Provider { return TOOL_MAPPING[providerID] ?? TOOLS } + function optionalToNullable(schema: z.ZodTypeAny): z.ZodTypeAny { + if (schema instanceof z.ZodObject) { + const shape = schema.shape + const newShape: Record = {} + + for (const [key, value] of Object.entries(shape)) { + const zodValue = value as z.ZodTypeAny + if (zodValue instanceof z.ZodOptional) { + newShape[key] = zodValue.unwrap().nullable() + } else { + newShape[key] = optionalToNullable(zodValue) + } + } + + return z.object(newShape) + } + + if (schema instanceof z.ZodArray) { + return z.array(optionalToNullable(schema.element)) + } + + if (schema instanceof z.ZodUnion) { + return z.union( + schema.options.map((option: z.ZodTypeAny) => + optionalToNullable(option), + ) as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]], + ) + } + + return schema + } + export const ModelNotFoundError = NamedError.create( "ProviderModelNotFoundError", z.object({ diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 0dd4e4e6..fe21b26c 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -497,7 +497,7 @@ export namespace Session { msgs.map(toUIMessage).filter((x) => x.parts.length > 0), ), ], - temperature: model.info.id === "codex-mini-latest" ? undefined : 0, + temperature: model.info.temperature ? 0 : undefined, tools: { ...tools, }, diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index e2502fa1..7bf1c595 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -35,7 +35,7 @@ export const BashTool = Tool.define({ .min(0) .max(MAX_TIMEOUT) .describe("Optional timeout in milliseconds") - .nullable(), + .optional(), description: z .string() .describe( diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index 78e29e28..26db51fa 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -21,7 +21,7 @@ export const EditTool = Tool.define({ ), replaceAll: z .boolean() - .nullable() + .optional() .describe("Replace all occurences of old_string (default false)"), }), async execute(params, ctx) { diff --git a/packages/opencode/src/tool/glob.ts b/packages/opencode/src/tool/glob.ts index 92a01f5e..aab5603e 100644 --- a/packages/opencode/src/tool/glob.ts +++ b/packages/opencode/src/tool/glob.ts @@ -11,7 +11,7 @@ export const GlobTool = Tool.define({ pattern: z.string().describe("The glob pattern to match files against"), path: z .string() - .nullable() + .optional() .describe( `The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.`, ), diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index 20199a5c..02979b14 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -14,13 +14,13 @@ export const GrepTool = Tool.define({ .describe("The regex pattern to search for in file contents"), path: z .string() - .nullable() + .optional() .describe( "The directory to search in. Defaults to the current working directory.", ), include: z .string() - .nullable() + .optional() .describe( 'File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")', ), diff --git a/packages/opencode/src/tool/ls.ts b/packages/opencode/src/tool/ls.ts index 42b78d42..0a0c4155 100644 --- a/packages/opencode/src/tool/ls.ts +++ b/packages/opencode/src/tool/ls.ts @@ -29,11 +29,11 @@ export const ListTool = Tool.define({ .describe( "The absolute path to the directory to list (must be absolute, not relative)", ) - .nullable(), + .optional(), ignore: z .array(z.string()) .describe("List of glob patterns to ignore") - .nullable(), + .optional(), }), async execute(params) { const app = App.info() diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 38091b7a..69d2a036 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -19,11 +19,11 @@ export const ReadTool = Tool.define({ offset: z .number() .describe("The line number to start reading from (0-based)") - .nullable(), + .optional(), limit: z .number() .describe("The number of lines to read (defaults to 2000)") - .nullable(), + .optional(), }), async execute(params, ctx) { let filePath = params.filePath diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index 0c7f2e62..77b673a3 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -22,7 +22,7 @@ export const WebFetchTool = Tool.define({ .min(0) .max(MAX_TIMEOUT / 1000) .describe("Optional timeout in seconds (max 120)") - .nullable(), + .optional(), }), async execute(params, ctx) { // Validate URL diff --git a/packages/opencode/test/tool/tool.test.ts b/packages/opencode/test/tool/tool.test.ts index 273e725c..2d41405d 100644 --- a/packages/opencode/test/tool/tool.test.ts +++ b/packages/opencode/test/tool/tool.test.ts @@ -9,7 +9,7 @@ describe("tool.glob", () => { let result = await GlobTool.execute( { pattern: "./node_modules/**/*", - path: null, + path: undefined, }, { sessionID: "test", @@ -25,7 +25,7 @@ describe("tool.glob", () => { let result = await GlobTool.execute( { pattern: "*.json", - path: null, + path: undefined, }, { sessionID: "test", diff --git a/patches/ai@4.3.16.patch b/patches/ai@4.3.16.patch new file mode 100644 index 00000000..7d6df589 --- /dev/null +++ b/patches/ai@4.3.16.patch @@ -0,0 +1,13 @@ +diff --git a/dist/index.mjs b/dist/index.mjs +index 92a80377692488c4ba8801ce33e7736ad7055e43..add6281bbecaa1c03d3b48eb99aead4a7a7336b2 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -1593,7 +1593,7 @@ function prepareCallSettings({ + return { + maxTokens, + // TODO v5 remove default 0 for temperature +- temperature: temperature != null ? temperature : 0, ++ temperature: temperature, + topP, + topK, + presencePenalty,