From 3c6dcad2afb6bc65a97affb4e72023465664509d Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:48:36 -0600 Subject: [PATCH 1/8] Fixed OPENCODE_CONFIG_DIR to load config files. (#4400) Co-authored-by: opencode-agent[bot] Co-authored-by: rekram1-node --- packages/opencode/src/config/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 440d95818..c7814721f 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -75,7 +75,7 @@ export namespace Config { for (const dir of directories) { await assertValid(dir) - if (dir.endsWith(".opencode")) { + if (dir.endsWith(".opencode") || dir === Flag.OPENCODE_CONFIG_DIR) { for (const file of ["opencode.jsonc", "opencode.json"]) { log.debug(`loading config from ${path.join(dir, file)}`) result = mergeDeep(result, await loadFile(path.join(dir, file))) From 58cc5cdf2a8b5233a4859d1b0cec47e4e7478878 Mon Sep 17 00:00:00 2001 From: Youssef Achy <19510452+PanAchy@users.noreply.github.com> Date: Mon, 17 Nov 2025 00:01:45 -0600 Subject: [PATCH 2/8] add support for azure cognitive services provider (#4397) --- packages/opencode/src/provider/provider.ts | 16 ++++++ packages/web/src/content/docs/providers.mdx | 56 +++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 185e3a9aa..b15df8b93 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -78,6 +78,22 @@ export namespace Provider { options: {}, } }, + "azure-cognitive-services": async () => { + const resourceName = process.env["AZURE_COGNITIVE_SERVICES_RESOURCE_NAME"] + return { + autoload: false, + async getModel(sdk: any, modelID: string, options?: Record) { + if (options?.["useCompletionUrls"]) { + return sdk.chat(modelID) + } else { + return sdk.responses(modelID) + } + }, + options: { + baseURL: resourceName ? `https://${resourceName}.cognitiveservices.azure.com/openai` : undefined, + }, + } + }, "amazon-bedrock": async () => { if (!process.env["AWS_PROFILE"] && !process.env["AWS_ACCESS_KEY_ID"] && !process.env["AWS_BEARER_TOKEN_BEDROCK"]) return { autoload: false } diff --git a/packages/web/src/content/docs/providers.mdx b/packages/web/src/content/docs/providers.mdx index d75c75d50..84a815490 100644 --- a/packages/web/src/content/docs/providers.mdx +++ b/packages/web/src/content/docs/providers.mdx @@ -229,6 +229,62 @@ Or if you already have an API key, you can select **Manually enter API Key** and --- +### Azure Cognitive Services + +1. Head over to the [Azure portal](https://portal.azure.com/) and create an **Azure OpenAI** resource. You'll need: + - **Resource name**: This becomes part of your API endpoint (`https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/`) + - **API key**: Either `KEY 1` or `KEY 2` from your resource + +2. Go to [Azure AI Foundry](https://ai.azure.com/) and deploy a model. + + :::note + The deployment name must match the model name for opencode to work properly. + ::: + +3. Run `opencode auth login` and select **Azure**. + + ```bash + $ opencode auth login + + ┌ Add credential + │ + ◆ Select provider + │ ● Azure Cognitive Services + │ ... + └ + ``` + +4. Enter your API key. + + ```bash + $ opencode auth login + + ┌ Add credential + │ + ◇ Select provider + │ Azure Cognitive Services + │ + ◇ Enter your API key + │ _ + └ + ``` + +5. Set your resource name as an environment variable: + + ```bash + AZURE_COGNITIVE_SERVICES_RESOURCE_NAME=XXX opencode + ``` + + Or add it to your bash profile: + + ```bash title="~/.bash_profile" + export AZURE_COGNITIVE_SERVICES_RESOURCE_NAME=XXX + ``` + +6. Run the `/models` command to select your deployed model. + +--- + ### Baseten 1. Head over to the [Baseten](https://app.baseten.co/), create an account, and generate an API key. From 5c722bf8c462936ef91602901da71ddbf7c519d7 Mon Sep 17 00:00:00 2001 From: Spoon <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Mon, 17 Nov 2025 07:02:05 +0100 Subject: [PATCH 3/8] fix(batch): simple UX feedback (#4396) --- packages/opencode/src/tool/batch.ts | 86 +++++++++++++++++------------ 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/packages/opencode/src/tool/batch.ts b/packages/opencode/src/tool/batch.ts index 45c62eb29..7d6449e7d 100644 --- a/packages/opencode/src/tool/batch.ts +++ b/packages/opencode/src/tool/batch.ts @@ -17,7 +17,6 @@ export const BatchTool = Tool.define("batch", async () => { }), ) .min(1, "Provide at least one tool call") - .max(10, "Too many tools in batch. Maximum allowed is 10.") .describe("Array of tool calls to execute in parallel"), }), formatValidationError(error) { @@ -34,34 +33,16 @@ export const BatchTool = Tool.define("batch", async () => { const { Session } = await import("../session") const { Identifier } = await import("../id/id") - const toolCalls = params.tool_calls + const toolCalls = params.tool_calls.slice(0, 10) + const discardedCalls = params.tool_calls.slice(10) const { ToolRegistry } = await import("./registry") const availableTools = await ToolRegistry.tools("", "") const toolMap = new Map(availableTools.map((t) => [t.id, t])) - const partIDs = new Map<(typeof toolCalls)[0], string>() - for (const call of toolCalls) { - const partID = Identifier.ascending("part") - partIDs.set(call, partID) - Session.updatePart({ - id: partID, - messageID: ctx.messageID, - sessionID: ctx.sessionID, - type: "tool", - tool: call.tool, - callID: partID, - state: { - status: "pending", - input: call.parameters, - raw: JSON.stringify(call), - }, - }) - } - const executeCall = async (call: (typeof toolCalls)[0]) => { const callStartTime = Date.now() - const partID = partIDs.get(call)! + const partID = Identifier.ascending("part") try { if (DISALLOWED.has(call.tool)) { @@ -77,6 +58,22 @@ export const BatchTool = Tool.define("batch", async () => { } const validatedParams = tool.parameters.parse(call.parameters) + await Session.updatePart({ + id: partID, + messageID: ctx.messageID, + sessionID: ctx.sessionID, + type: "tool", + tool: call.tool, + callID: partID, + state: { + status: "running", + input: call.parameters, + time: { + start: callStartTime, + }, + }, + }) + const result = await tool.execute(validatedParams, { ...ctx, callID: partID }) await Session.updatePart({ @@ -126,31 +123,48 @@ export const BatchTool = Tool.define("batch", async () => { const results = await Promise.all(toolCalls.map((call) => executeCall(call))) - const successfulCalls = results.filter((r) => r.success).length - const failedCalls = toolCalls.length - successfulCalls + // Add discarded calls as errors + const now = Date.now() + for (const call of discardedCalls) { + const partID = Identifier.ascending("part") + await Session.updatePart({ + id: partID, + messageID: ctx.messageID, + sessionID: ctx.sessionID, + type: "tool", + tool: call.tool, + callID: partID, + state: { + status: "error", + input: call.parameters, + error: "Maximum of 10 tools allowed in batch", + time: { start: now, end: now }, + }, + }) + results.push({ + success: false as const, + tool: call.tool, + error: new Error("Maximum of 10 tools allowed in batch"), + }) + } - const outputParts = results.map((r) => { - if (r.success) { - return `\n${r.result.output}\n` - } - const errorMessage = r.error instanceof Error ? r.error.message : String(r.error) - return `\nError: ${errorMessage}\n` - }) + const successfulCalls = results.filter((r) => r.success).length + const failedCalls = results.length - successfulCalls const outputMessage = failedCalls > 0 - ? `Executed ${successfulCalls}/${toolCalls.length} tools successfully. ${failedCalls} failed.\n\n${outputParts.join("\n\n")}` - : `All ${successfulCalls} tools executed successfully.\n\n${outputParts.join("\n\n")}\n\nKeep using the batch tool for optimal performance in your next response!` + ? `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.` + : `All ${successfulCalls} tools executed successfully.\n\nKeep using the batch tool for optimal performance in your next response!` return { - title: `Batch execution (${successfulCalls}/${toolCalls.length} successful)`, + title: `Batch execution (${successfulCalls}/${results.length} successful)`, output: outputMessage, attachments: results.filter((result) => result.success).flatMap((r) => r.result.attachments ?? []), metadata: { - totalCalls: toolCalls.length, + totalCalls: results.length, successful: successfulCalls, failed: failedCalls, - tools: toolCalls.map((c) => c.tool), + tools: params.tool_calls.map((c) => c.tool), details: results.map((r) => ({ tool: r.tool, success: r.success })), }, } From e96442310cad3fff0158d432e6ce792fa580dbff Mon Sep 17 00:00:00 2001 From: Tyler Gannon Date: Mon, 17 Nov 2025 00:06:40 -0600 Subject: [PATCH 4/8] chore: replace z.union with z.enum for cleaner OpenAPI generation (#4394) --- packages/opencode/src/agent/agent.ts | 2 +- packages/opencode/src/config/config.ts | 4 ++-- packages/opencode/src/server/server.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 867bc0fe9..740f67b7e 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -12,7 +12,7 @@ export namespace Agent { .object({ name: z.string(), description: z.string().optional(), - mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]), + mode: z.enum(["subagent", "primary", "all"]), builtIn: z.boolean(), topP: z.number().optional(), temperature: z.number().optional(), diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index c7814721f..51aa914a3 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -337,7 +337,7 @@ export namespace Config { export const Mcp = z.discriminatedUnion("type", [McpLocal, McpRemote]) export type Mcp = z.infer - export const Permission = z.union([z.literal("ask"), z.literal("allow"), z.literal("deny")]) + export const Permission = z.enum(["ask", "allow", "deny"]) export type Permission = z.infer export const Command = z.object({ @@ -358,7 +358,7 @@ export namespace Config { tools: z.record(z.string(), z.boolean()).optional(), disable: z.boolean().optional(), description: z.string().optional().describe("Description of when to use the agent"), - mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]).optional(), + mode: z.enum(["subagent", "primary", "all"]).optional(), color: z .string() .regex(/^#[0-9a-fA-F]{6}$/, "Invalid hex color format") diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index a4b2b1701..9ed1cac67 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -50,7 +50,7 @@ const ERRORS = { schema: resolver( z .object({ - data: z.any().nullable(), + data: z.any(), errors: z.array(z.record(z.string(), z.any())), success: z.literal(false), }) From 10b3702938c19a405f504dd0268087f262ab6a99 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 17 Nov 2025 00:07:23 -0600 Subject: [PATCH 5/8] chore: update type --- packages/sdk/js/src/gen/types.gen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 433e61bfc..ea43490ac 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1143,7 +1143,7 @@ export type Config = { } export type BadRequestError = { - data: unknown | null + data: unknown errors: Array<{ [key: string]: unknown }> From f4d892d4e1f42734c5ecc438163ec1dccc2ef7fc Mon Sep 17 00:00:00 2001 From: Keath Milligan Date: Mon, 17 Nov 2025 01:06:44 -0600 Subject: [PATCH 6/8] fix: handle Git Bash path mapping on windows (#4380) --- packages/opencode/src/tool/bash.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index a3ccfc397..f184d5efe 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -89,10 +89,18 @@ export const BashTool = Tool.define("bash", { .text() .then((x) => x.trim()) log.info("resolved path", { arg, resolved }) - if (resolved && !Filesystem.contains(Instance.directory, resolved)) { - throw new Error( - `This command references paths outside of ${Instance.directory} so it is not allowed to be executed.`, - ) + if (resolved) { + // Git Bash on Windows returns Unix-style paths like /c/Users/... + const normalized = + process.platform === "win32" && resolved.match(/^\/[a-z]\//) + ? resolved.replace(/^\/([a-z])\//, (_, drive) => `${drive.toUpperCase()}:\\`).replace(/\//g, "\\") + : resolved + + if (!Filesystem.contains(Instance.directory, normalized)) { + throw new Error( + `This command references paths outside of ${Instance.directory} so it is not allowed to be executed.`, + ) + } } } } From 5731c268b6c6cc00ab288ca2118f469fb1a6bc1e Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Mon, 17 Nov 2025 17:08:22 +1000 Subject: [PATCH 7/8] fix: Line count on win (#4401) --- .../opencode/src/cli/cmd/tui/component/prompt/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index a7f248758..b33f2ac63 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -665,7 +665,11 @@ export function Prompt(props: PromptProps) { return } - const pastedContent = event.text.trim() + // Normalize line endings at the boundary + // Windows ConPTY/Terminal often sends CR-only newlines in bracketed paste + // Replace CRLF first, then any remaining CR + const normalizedText = event.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n") + const pastedContent = normalizedText.trim() if (!pastedContent) { command.trigger("prompt.paste") return From 9fd43ec6161ee5bc782f83a3cfe190bddbb578cd Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 17 Nov 2025 12:04:41 +0000 Subject: [PATCH 8/8] ignore: update download stats 2025-11-17 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 0cf4d185f..a4c491bf4 100644 --- a/STATS.md +++ b/STATS.md @@ -142,3 +142,4 @@ | 2025-11-14 | 759,928 (+10,023) | 705,237 (+9,080) | 1,465,165 (+19,103) | | 2025-11-15 | 765,955 (+6,027) | 712,870 (+7,633) | 1,478,825 (+13,660) | | 2025-11-16 | 771,069 (+5,114) | 716,596 (+3,726) | 1,487,665 (+8,840) | +| 2025-11-17 | 780,161 (+9,092) | 723,339 (+6,743) | 1,503,500 (+15,835) |