diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index e76583f2d3..c7990d1b35 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -225,6 +225,7 @@ const live: Layer.Layer< execute: async () => ({ output: "", title: "", metadata: {} }), }) } + const sortedTools = Object.fromEntries(Object.entries(tools).toSorted(([a], [b]) => a.localeCompare(b))) // Wire up toolExecutor for DWS workflow models so that tool calls // from the workflow service are executed via opencode's tool system @@ -238,7 +239,7 @@ const live: Layer.Layer< workflowModel.sessionID = input.sessionID workflowModel.systemPrompt = system.join("\n") workflowModel.toolExecutor = async (toolName, argsJson, _requestID) => { - const t = tools[toolName] + const t = sortedTools[toolName] if (!t || !t.execute) { return { result: "", error: `Unknown tool: ${toolName}` } } @@ -260,7 +261,7 @@ const live: Layer.Layer< } const ruleset = Permission.merge(input.agent.permission ?? [], input.permission ?? []) - workflowModel.sessionPreapprovedTools = Object.keys(tools).filter((name) => { + workflowModel.sessionPreapprovedTools = Object.keys(sortedTools).filter((name) => { const match = ruleset.findLast((rule) => Wildcard.match(name, rule.permission)) return !match || match.action !== "ask" }) @@ -341,7 +342,7 @@ const live: Layer.Layer< }, async experimental_repairToolCall(failed) { const lower = failed.toolCall.toolName.toLowerCase() - if (lower !== failed.toolCall.toolName && tools[lower]) { + if (lower !== failed.toolCall.toolName && sortedTools[lower]) { l.info("repairing tool call", { tool: failed.toolCall.toolName, repaired: lower, @@ -364,8 +365,8 @@ const live: Layer.Layer< topP: params.topP, topK: params.topK, providerOptions: ProviderTransform.providerOptions(input.model, params.options), - activeTools: Object.keys(tools).filter((x) => x !== "invalid"), - tools, + activeTools: Object.keys(sortedTools).filter((x) => x !== "invalid"), + tools: sortedTools, toolChoice: input.toolChoice, maxOutputTokens: params.maxOutputTokens, abortSignal: input.abort,