#18528 · @Quadina · opened Mar 21, 2026 at 1:55 PM UTC · last updated Mar 21, 2026 at 1:56 PM UTC

feat(task): add model override for subagents

appfeat
58
+22155 files

Score breakdown

Impact

9.0

Clarity

8.0

Urgency

4.0

Ease Of Review

8.0

Guidelines

8.0

Readiness

7.0

Size

4.0

Trust

5.0

Traction

2.0

Summary

This PR adds a model parameter to the task tool, allowing orchestrators to dynamically select which model a subagent uses at runtime. The feature is gated by a model_override permission for security, defaulting to deny. This enhances agent flexibility for use cases like adaptive review or variable-complexity reports.

Open in GitHub

Description

Issue for this PR

Closes #17595

Type of change

  • [ ] Bug fix
  • [x] New feature
  • [ ] Refactor / code improvement
  • [ ] Documentation

What does this PR do?

Adds a model parameter to the task tool so the orchestrator can pick which model a subagent runs on ("provider/model-id" format). Gated by a model_override permission — denied by default, users opt-in via config with support for wildcards like "anthropic/*": "allow".

Without permission the override is silently ignored and the subagent uses its default model.

How did you verify your code works?

bun test test/tool/task.test.ts — 14 tests pass (parseModel parsing + permission evaluation). bun typecheck — clean.

Screenshots / recordings

N/A — no UI changes.

Checklist

  • [x] I have tested my changes locally
  • [x] I have not included unrelated changes in this PR

Linked Issues

#17595 [FEATURE]: Runtime model override for task tool subagents

View issue

Comments

No comments.

Changed Files

packages/opencode/src/agent/agent.ts

+10
@@ -57,6 +57,7 @@ export namespace Agent {
const defaults = Permission.fromConfig({
"*": "allow",
doom_loop: "ask",
model_override: "deny",
external_directory: {
"*": "ask",
...Object.fromEntries(whitelistedDirs.map((dir) => [dir, "allow"])),

packages/opencode/src/config/config.ts

+10
@@ -671,6 +671,7 @@ export namespace Config {
list: PermissionRule.optional(),
bash: PermissionRule.optional(),
task: PermissionRule.optional(),
model_override: PermissionRule.optional(),
external_directory: PermissionRule.optional(),
todowrite: PermissionAction.optional(),
todoread: PermissionAction.optional(),

packages/opencode/src/tool/task.ts

+383
@@ -11,6 +11,17 @@ import { iife } from "@/util/iife"
import { defer } from "@/util/defer"
import { Config } from "../config/config"
import { Permission } from "@/permission"
import type { ModelID } from "../provider/schema"
import type { ProviderID } from "../provider/schema"
export function parseModel(raw: string): { providerID: ProviderID; modelID: ModelID } {
const idx = raw.indexOf("/")
if (idx === -1) throw new Error(`Invalid model format "${raw}". Expected "provider/model-id"`)
return {
providerID: raw.slice(0, idx) as ProviderID,
modelID: raw.slice(idx + 1) as ModelID,
}
}
const parameters = z.object({
description: z.string().describe("A short (3-5 words) description of the task"),
@@ -23,6 +34,12 @@ const parameters = z.object({
)
.optional(),
command: z.string().describe("The command that triggered this task").optional(),
model: z
.string()
.describe(
'Override the model for this task. Format: "provider/model-id" (e.g. "anthropic/claude-opus-4-0520", "anthropic/claude-haiku-4-20250514", "google/gemini-2.5-pro"). If not specified, the subagent\'s configured model or the parent\'s model is used.',
)

packages/opencode/src/tool/task.txt

+61
@@ -5,6 +5,12 @@ Available agent types and the tools they have access to:
When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
You can optionally specify a model to override which AI model the subagent uses for this task.
Format: "provider/model-id" (e.g. "anthropic/claude-opus-4-0520", "anthropic/claude-haiku-4-20250514", "google/gemini-2.5-pro").
Use this when the user explicitly requests a task to run on a specific model (e.g. "do this on opus", "use haiku for that").
If not specified, the subagent's configured model is used, or the current model is inherited from the parent.
Note: model overrides require the "model_override" permission. By default this permission is denied and the user will be asked to approve. The user can allow specific models or providers in their config (e.g. "model_override": { "anthropic/*": "allow" }).
When to use the Task tool:
- When you are instructed to execute custom slash commands. Use the Task tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description="Check the file", prompt="/check-file path/to/file.py")
@@ -14

packages/opencode/test/tool/task.test.ts

+1751
@@ -1,13 +1,56 @@
import { afterEach, describe, expect, test } from "bun:test"
import { Agent } from "../../src/agent/agent"
import { Instance } from "../../src/project/instance"
import { TaskTool } from "../../src/tool/task"
import { Permission } from "../../src/permission"
import { parseModel, TaskTool } from "../../src/tool/task"
import { tmpdir } from "../fixture/fixture"
afterEach(async () => {
await Instance.disposeAll()
})
describe("tool.task parseModel", () => {
test("parses valid provider/model format", () => {
const result = parseModel("anthropic/claude-opus-4-0520")
expect(String(result.providerID)).toBe("anthropic")
expect(String(result.modelID)).toBe("claude-opus-4-0520")
})
test("parses model with nested slashes", () => {
const result = parseModel("amazon-bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0")
expect(String(result.providerID)).toBe("amazon-bedrock")
expect(String(result.modelID)).toBe("us.anthropic.claude-sonnet-4-20250514-v1:0")
})
test("parses simple provider/model", () => {
const result = parseModel("openai/gpt-4")
expect(String(result.providerID)).toBe("openai")
expect(S