Can you hide the opencode zen models from the model selection menu?
Can you hide the opencode zen models from the model selection menu?
I'll help you hide the opencode zen models from the model selection menu. Let me first explore the codebase to understand where models are configured and displayed.
task_id: ses_3ccb064f9ffeELoF22IentDpwi (for resuming to continue this task if needed)
<task_result>
Based on my comprehensive search of the codebase, here are my findings:
---
## Summary: Model Configuration and Filtering in OpenCode
### 1. "zen" Models References
**Found in `/home/jm/data/code/opencode/packages/opencode/test/tool/fixtures/models-api.json`:**
- Line 20980: `"api": "https://opencode.ai/zen/v1"`
- Line 20982: `"doc": "https://opencode.ai/docs/zen"`
There are references to an "opencode.ai/zen" API endpoint, which appears to be a specific model endpoint.
### 2. Model Selection Menu Implementation
**Primary location:** `/home/jm/data/code/opencode/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx`
This is the main TUI component that handles model selection. Key features:
- Shows favorites, recents, and all providers/models
- Supports fuzzy search
- Has filtering logic at **lines 64, 97, 140, 241**:
```typescript
disabled: provider.id === "opencode" && model.id.includes("-nano")
```
### 3. How Models are Configured, Filtered, or Displayed
#### A. **Data Source**
- **`/home/jm/data/code/opencode/packages/opencode/src/provider/models.ts`** - Fetches models from `https://models.dev/api.json`
- **`/home/jm/data/code/opencode/packages/opencode/src/provider/models-snapshot.ts`** - Auto-generated snapshot for bundling
#### B. **Model Schema and Metadata**
Models have a schema with fields including:
- `id`, `name`, `family`
- `status`: `"alpha"`, `"beta"`, `"deprecated"`, or `"active"`
- `cost`, `limit`, `modalities`
- `experimental`: boolean
#### C. **Provider Registration and Custom Loaders**
**`/home/jm/data/code/opencode/packages/opencode/src/provider/provider.ts`**
- Line 407-417: `zenmux` provider registered in `CUSTOM_LOADERS`
- Line 102-123: Special handling for `opencode` provider - removes paid models if no API key exists
#### D. **Filtering Mechanisms in `provider.ts`**
**Line 936-959:** Multiple model deletion filters:...<file>
00001| import { createMemo, createSignal } from "solid-js"
00002| import { useLocal } from "@tui/context/local"
00003| import { useSync } from "@tui/context/sync"
00004| import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda"
00005| import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
00006| import { useDialog } from "@tui/ui/dialog"
00007| import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
00008| import { useKeybind } from "../context/keybind"
00009| import * as fuzzysort from "fuzzysort"
00010|
00011| export function useConnected() {
00012| const sync = useSync()
00013| return createMemo(() =>
00014| sync.data.provider.some((x) => x.id !== "opencode" || Object.values(x.models).some((y) => y.cost?.input !== 0)),
00015| )
00016| }
00017|
00018| export function DialogModel(props: { providerID?: string }) {
00019| const local = useLocal()
00020| const sync = useSync()
00021| const dialog = useDialog()
00022| const keybind = useKeybind()
00023| const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
00024| const [query, setQuery] = createSignal("")
00025|
00026| const connected = useConnected()
00027| const providers = createDialogProviderOptions()
00028|
00029| const showExtra = createMemo(() => {
00030| if (!connected()) return false
00031| if (props.providerID) return false
00032| return true
00033| })
00034|
00035| const options = createMemo(() => {
00036| const q = query()
00037| const needle = q.trim()
00038| const showSections = showExtra() && needle.length === 0
00039| const favorites = connected() ? local.model.favorite() : []
00040| const recents = local.model.recent()
00041|
00042| const recentList = showSections
00043| ? recents.filter(
00044| (item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID),
00045| )
00046| : []
00047|...<file>
00931| continue
00932| }
00933|
00934| const configProvider = config.provider?.[providerID]
00935|
00936| for (const [modelID, model] of Object.entries(provider.models)) {
00937| model.api.id = model.api.id ?? model.id ?? modelID
00938| if (modelID === "gpt-5-chat-latest" || (providerID === "openrouter" && modelID === "openai/gpt-5-chat"))
00939| delete provider.models[modelID]
00940| if (model.status === "alpha" && !Flag.OPENCODE_ENABLE_EXPERIMENTAL_MODELS) delete provider.models[modelID]
00941| if (model.status === "deprecated") delete provider.models[modelID]
00942| if (
00943| (configProvider?.blacklist && configProvider.blacklist.includes(modelID)) ||
00944| (configProvider?.whitelist && !configProvider.whitelist.includes(modelID))
00945| )
00946| delete provider.models[modelID]
00947|
00948| model.variants = mapValues(ProviderTransform.variants(model), (v) => v)
00949|
00950| // Filter out disabled variants from config
00951| const configVariants = configProvider?.models?.[modelID]?.variants
00952| if (configVariants && model.variants) {
00953| const merged = mergeDeep(model.variants, configVariants)
00954| model.variants = mapValues(
00955| pickBy(merged, (v) => !v.disabled),
00956| (v) => omit(v, ["disabled"]),
00957| )
00958| }
00959| }
00960|
00961| if (Object.keys(provider.models).length === 0) {
00962| delete providers[providerID]
00963| continue
00964| }
00965|
00966| log.info("found", { providerID })
00967| }
00968|
00969| return {
00970| models: languages,
(File has more lines. Use 'offset' parameter to read beyond line 970)
</file><file>
01036| "Automatically update to the latest version. Set to true to auto-update, false to disable, or 'notify' to show update notifications",
01037| ),
01038| disabled_providers: z.array(z.string()).optional().describe("Disable providers that are loaded automatically"),
01039| enabled_providers: z
01040| .array(z.string())
01041| .optional()
01042| .describe("When set, ONLY these providers will be enabled. All other providers will be ignored"),
01043| model: z.string().describe("Model to use in the format of provider/model, eg anthropic/claude-2").optional(),
01044| small_model: z
01045| .string()
01046| .describe("Small model to use for tasks like title generation in the format of provider/model")
01047| .optional(),
01048| default_agent: z
01049| .string()
01050| .optional()
(File has more lines. Use 'offset' parameter to read beyond line 1050)
</file>All 3 tools executed successfully.
Keep using the batch tool for optimal performance in your next response!<file>
00401| options: envServiceKey ? { deploymentId, resourceGroup } : {},
00402| async getModel(sdk: any, modelID: string) {
00403| return sdk(modelID)
00404| },
00405| }
00406| },
00407| zenmux: async () => {
00408| return {
00409| autoload: false,
00410| options: {
00411| headers: {
00412| "HTTP-Referer": "https://opencode.ai/",
00413| "X-Title": "opencode",
00414| },
00415| },
00416| }
00417| },
00418| gitlab: async (input) => {
00419| const instanceUrl = Env.get("GITLAB_INSTANCE_URL") || "https://gitlab.com"
00420|
(File has more lines. Use 'offset' parameter to read beyond line 420)
</file>Based on my exploration, I found that the zen models belong to the zenmux provider. Here are three approaches to hide them:
Add to opencode.json:
{
"disabled_providers": ["zenmux"]
}
Or use enabled_providers to explicitly allow only specific providers:
{
"enabled_providers": ["opencode", "anthropic", "openai", ...]
}
Modify /home/jm/data/code/opencode/packages/opencode/src/provider/provider.ts around line 936:
Add providerID === "zenmux" to the deletion logic to remove all zenmux models at the data loading stage.
Modify /home/jm/data/code/opencode/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx:
Add provider.id === "zenmux" check to the disabled property (lines 64, 97, 140) to visually disable zenmux models in the selection menu.
Which approach would you prefer?
3
I'll create a comprehensive plan for implementing UI-level filtering to hide zen models from the model selection menu.
<file>
00061| title: model.name ?? item.modelID,
00062| description: provider.name,
00063| category: "Favorites",
00064| disabled: provider.id === "opencode" && model.id.includes("-nano"),
00065| footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
00066| onSelect: () => {
00067| dialog.clear()
00068| local.model.set(
00069| {
00070| providerID: provider.id,
(File has more lines. Use 'offset' parameter to read beyond line 70)
</file><file>
00094| title: model.name ?? item.modelID,
00095| description: provider.name,
00096| category: "Recent",
00097| disabled: provider.id === "opencode" && model.id.includes("-nano"),
00098| footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
00099| onSelect: () => {
00100| dialog.clear()
00101| local.model.set(
00102| {
00103| providerID: provider.id,
(File has more lines. Use 'offset' parameter to read beyond line 103)
</file><file>
00137| ? "(Favorite)"
00138| : undefined,
00139| category: connected() ? provider.name : undefined,
00140| disabled: provider.id === "opencode" && model.includes("-nano"),
00141| footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
00142| onSelect() {
00143| dialog.clear()
00144| local.model.set(
00145| {
00146| providerID: provider.id,
(File has more lines. Use 'offset' parameter to read beyond line 146)
</file>No files found/home/jm/data/code/opencode/packages/opencode/test/provider/copilot/copilot-chat-model.test.ts
<file>
00001| # opencode agent guidelines
00002|
00003| ## Build/Test Commands
00004|
00005| - **Install**: `bun install`
00006| - **Run**: `bun run --conditions=browser ./src/index.ts`
00007| - **Typecheck**: `bun run typecheck` (npm run typecheck)
00008| - **Test**: `bun test` (runs all tests)
00009| - **Single test**: `bun test test/tool/tool.test.ts` (specific test file)
00010|
00011| ## Code Style
00012|
00013| - **Runtime**: Bun with TypeScript ESM modules
00014| - **Imports**: Use relative imports for local modules, named imports preferred
00015| - **Types**: Zod schemas for validation, TypeScript interfaces for structure
00016| - **Naming**: camelCase for variables/functions, PascalCase for classes/namespaces
00017| - **Error handling**: Use Result patterns, avoid throwing exceptions in tools
00018| - **File structure**: Namespace-based organization (e.g., `Tool.define()`, `Session.create()`)
00019|
00020| ## Architecture
00021|
00022| - **Tools**: Implement `Tool.Info` interface with `execute()` method
00023| - **Context**: Pass `sessionID` in tool context, use `App.provide()` for DI
00024| - **Validation**: All inputs validated with Zod schemas
00025| - **Logging**: Use `Log.create({ service: "name" })` pattern
00026| - **Storage**: Use `Storage` namespace for persistence
00027| - **API Client**: The TypeScript TUI (built with SolidJS + OpenTUI) communicates with the OpenCode server using `@opencode-ai/sdk`. When adding/modifying server endpoints in `packages/opencode/src/server/server.ts`, run `./script/generate.ts` to regenerate the SDK and related files.
00028|
(End of file - total 28 lines)
</file>Add provider.id === "zenmux" check to the disabled property in the model selection dialog to visually hide all zenmux provider models from the UI.
File: /home/jm/data/code/opencode/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
Three locations need to be modified:
// Current:
disabled: provider.id === "opencode" && model.id.includes("-nano"),
// Change to:
disabled: (provider.id === "opencode" && model.id.includes("-nano")) || provider.id === "zenmux",
// Current:
disabled: provider.id === "opencode" && model.id.includes("-nano"),
// Change to:
disabled: (provider.id === "opencode" && model.id.includes("-nano")) || provider.id === "zenmux",
// Current:
disabled: provider.id === "opencode" && model.includes("-nano"),
// Change to:
disabled: (provider.id === "opencode" && model.includes("-nano")) || provider.id === "zenmux",
After making changes, run:
bun run typecheck
opencode.json)"disabled_providers": ["zenmux"] in opencode.jsonprovider.ts line 936Ready to proceed with implementation?
Yes
I'm ready to execute the plan. However, I'm currently in READ-ONLY plan mode and cannot make any file modifications.
To proceed with the implementation, please:
Once confirmed, I will:
disabled property checks in dialog-model.tsx (lines 64, 97, 140)bun run typecheck to verify the changesHave the @coder-agent implement it
Use the above message and context to generate a prompt and call the task tool with subagent: coder-agent
task_id: ses_3cc0f3afaffe9783A8yTKQFoQh (for resuming to continue this task if needed)
<task_result>
Perfect! All three changes have been successfully applied and verified:
✅ **Line 64 (Favorites section)**: Updated to disable zenmux models
✅ **Line 97 (Recent models section)**: Updated to disable zenmux models
✅ **Line 140 (All providers section)**: Updated to disable zenmux models
✅ **Typecheck passed**: All changes are type-safe
The zenmux provider models will now appear as disabled in the model selection dialog across all three sections (Favorites, Recent, and All providers), while still being accessible via configuration if needed.
</task_result>Done! The coder-agent has successfully implemented the changes:
disabled property checks updated to hide zenmux models (lines 64, 97, 140)Zen models will now appear as disabled in the model selection menu.