Hide opencode zen models from menu

/home/jm/data/code/opencode/packages/opencode · 15 messages · 59 parts · · 149,365 tokens
Created Feb 6, 02:16:12 PM · Updated Feb 6, 05:13:33 PM · 58 syncs
userFeb 6, 02:16:12 PM

Can you hide the opencode zen models from the model selection menu?

assistantFeb 6, 02:16:13 PM
glm-4.7 · zai-coding-plan
The user wants to hide the "opencode zen models" from the model selection menu. I'm in plan mode, so I need to: 1. First understand what "opencode zen models" are 2. Find where the model selection menu is implemented 3. Determine how to hide these models 4. Create a plan for the implementation Let me start by searching for: - "zen" in the codebase to find references to these models - Model selection UI components - How models are configured or displayed I should use the explore agent or search tools to understand the codebase structure.

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.

taskcompleted
Find zen model references
Output
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:...
16,702in / 253out / 123reasoning
assistantFeb 6, 02:21:02 PM
glm-4.7 · zai-coding-plan
Great! Now I understand the codebase structure. The explore agent found several ways to hide models: 1. **Config-based approach** (easiest, no code changes): - Use `disabled_providers` to disable the "zenmux" provider entirely - Use `blacklist`/`whitelist` at the provider level to filter specific models 2. **Code-based approach** (permanent): - Add filtering logic in `dialog-model.tsx` to disable models in the UI - Add model deletion logic in `provider.ts` to remove models at the data level Let me read the key files to understand the current implementation better, especially: 1. The dialog-model.tsx to see how the current filtering works 2. The provider.ts to see how models are filtered at the data level 3. The config schema to understand the configuration options This will help me present a clear plan to the user with tradeoffs.
readcompleted
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
Output
<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|...
readcompleted
packages/opencode/src/provider/provider.ts
Output
<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>
readcompleted
packages/opencode/src/config/config.ts
Output
<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>
batchcompleted
Batch execution (3/3 successful)
Output
All 3 tools executed successfully.

Keep using the batch tool for optimal performance in your next response!
18,474in / 325out / 195reasoning
assistantFeb 6, 02:21:16 PM
glm-4.7 · zai-coding-plan
Now I have a good understanding of the codebase. Let me also check what provider the "zen" models belong to by looking at the provider registration and checking if "zenmux" is indeed the provider.
readcompleted
packages/opencode/src/provider/provider.ts
Output
<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>
3,388in / 78out / 42reasoning
assistantFeb 6, 02:21:21 PM
glm-4.7 · zai-coding-plan
I can see that "zenmux" is a registered provider. Now I have enough information to create a comprehensive plan for the user. Let me present the options: 1. **Config-based (Recommended)** - Add a config entry to disable the zenmux provider 2. **Code-level filtering** - Add a check to filter out zen models in the provider loading logic 3. **UI-level filtering** - Add a disabled check in the dialog-model.tsx I should ask the user which approach they prefer, or if they have a preference. The config-based approach is the cleanest and most flexible, but code-level filtering ensures it's always hidden.

Based on my exploration, I found that the zen models belong to the zenmux provider. Here are three approaches to hide them:

Option 1: Config-based (Recommended - No Code Changes)

Add to opencode.json:

{
  "disabled_providers": ["zenmux"]
}

Or use enabled_providers to explicitly allow only specific providers:

{
  "enabled_providers": ["opencode", "anthropic", "openai", ...]
}

Option 2: Code-level Filtering (Permanent)

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.

Option 3: UI-level Filtering

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?

  • Option 1 - Config-based (flexible, per-user control)
  • Option 2 - Code-level filtering (permanent, applies to all users)
  • Option 3 - UI-level filtering (visual only, models still accessible)
239in / 436out / 133reasoning
userFeb 6, 02:24:24 PM

3

assistantFeb 6, 02:24:24 PM
glm-4.7 · zai-coding-plan
The user selected Option 3 - UI-level filtering. However, I'm in plan mode, so I need to create a comprehensive plan for implementing this approach without actually making any changes. Let me think about what needs to be done for Option 3 (UI-level filtering): 1. Modify `/home/jm/data/code/opencode/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx` 2. Add `provider.id === "zenmux"` check to the `disabled` property at three locations: - Line 64 (favorites section) - Line 97 (recents section) - Line 140 (all providers section) The current filter is: ```typescript disabled: provider.id === "opencode" && model.id.includes("-nano") ``` I need to change it to also disable zenmux: ```typescript disabled: provider.id === "opencode" && model.id.includes("-nano") || provider.id === "zenmux" ``` Actually, looking at the code more carefully, I should use proper grouping: ```typescript disabled: (provider.id === "opencode" && model.id.includes("-nano")) || provider.id === "zenmux" ``` Let me read the file again to confirm the exact context and create a comprehensive plan.

I'll create a comprehensive plan for implementing UI-level filtering to hide zen models from the model selection menu.

readcompleted
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
Output
<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>
readcompleted
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
Output
<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>
readcompleted
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
Output
<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>
22,316in / 402out / 259reasoning
assistantFeb 6, 02:24:35 PM
glm-4.7 · zai-coding-plan
Perfect! I can now see the exact locations where I need to make changes. Let me create a comprehensive plan. The current disabled filter at all three locations (lines 64, 97, 140) is: - Lines 64 & 97: `disabled: provider.id === "opencode" && model.id.includes("-nano")` - Line 140: `disabled: provider.id === "opencode" && model.includes("-nano")` I need to change these to also disable zenmux: - Lines 64 & 97: `disabled: (provider.id === "opencode" && model.id.includes("-nano")) || provider.id === "zenmux"` - Line 140: `disabled: (provider.id === "opencode" && model.includes("-nano")) || provider.id === "zenmux"` I should also check if there are any tests related to this functionality.
grepcompleted
dialog-model|DialogModel
Output
No files found
bashcompleted
Find test files related to model dialog
Output
/home/jm/data/code/opencode/packages/opencode/test/provider/copilot/copilot-chat-model.test.ts
23,144in / 260out / 181reasoning
assistantFeb 6, 02:24:43 PM
glm-4.7 · zai-coding-plan
Good, there don't appear to be specific tests for the model dialog component. Now let me check the AGENTS.md to see if there are any testing guidelines or build/typecheck commands I should run after making changes.
readcompleted
packages/opencode/AGENTS.md
Output
<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>
32in / 68out / 44reasoning
assistantFeb 6, 02:24:48 PM
glm-4.7 · zai-coding-plan
Perfect! Now I have all the information I need to create a comprehensive plan. Let me structure the plan: ## Plan Summary Modify the `disabled` property in the model selection dialog to hide zenmux provider models by adding a check for `provider.id === "zenmux"`. ## Changes Required 1. File: `/home/jm/data/code/opencode/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx` - Line 64: Modify disabled filter in favorites section - Line 97: Modify disabled filter in recents section - Line 140: Modify disabled filter in all providers section ## Verification - Run `bun run typecheck` to ensure TypeScript types are correct Let me create the detailed plan now.

Plan: UI-Level Filtering to Hide Zen Models

Summary

Add provider.id === "zenmux" check to the disabled property in the model selection dialog to visually hide all zenmux provider models from the UI.

Changes Required

File: /home/jm/data/code/opencode/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx

Three locations need to be modified:

1. Line 64 - Favorites section

// Current:
disabled: provider.id === "opencode" && model.id.includes("-nano"),

// Change to:
disabled: (provider.id === "opencode" && model.id.includes("-nano")) || provider.id === "zenmux",

2. Line 97 - Recent models section

// Current:
disabled: provider.id === "opencode" && model.id.includes("-nano"),

// Change to:
disabled: (provider.id === "opencode" && model.id.includes("-nano")) || provider.id === "zenmux",

3. Line 140 - All providers section

// Current:
disabled: provider.id === "opencode" && model.includes("-nano"),

// Change to:
disabled: (provider.id === "opencode" && model.includes("-nano")) || provider.id === "zenmux",

Verification

After making changes, run:

bun run typecheck

Notes

  • This is a UI-level only change - zen models still exist in the provider data and can be accessed via config (e.g., if a user manually sets a zen model in opencode.json)
  • Models will appear as disabled/grayed out in the selection menu and won't be selectable
  • The filter applies to three sections: Favorites, Recent, and the full provider list

Alternative Approaches (if needed later)

  • Config-based: Use "disabled_providers": ["zenmux"] in opencode.json
  • Code-level: Delete zenmux models at data loading in provider.ts line 936

Ready to proceed with implementation?

1,230in / 602out / 154reasoning
userFeb 6, 05:08:20 PM

Yes

assistantFeb 6, 05:08:20 PM
glm-4.7 · zai-coding-plan
The user has confirmed they want to proceed with the implementation. However, I'm in READ-ONLY plan mode, so I cannot make any edits. I need to acknowledge their confirmation and wait for them to exit plan mode or give me explicit permission to execute. Since the user said "Yes", they are agreeing to the plan. I should acknowledge this and let them know I'm ready to execute once they exit plan mode or give permission.

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:

  • Exit plan mode, or
  • Give explicit permission to execute the changes

Once confirmed, I will:

  1. Update the three disabled property checks in dialog-model.tsx (lines 64, 97, 140)
  2. Run bun run typecheck to verify the changes
23,909in / 189out / 88reasoning
userFeb 6, 05:11:51 PM

Have the @coder-agent implement it

Use the above message and context to generate a prompt and call the task tool with subagent: coder-agent

assistantFeb 6, 05:11:51 PM
glm-4.7 · zai-coding-plan
The user wants me to have the coder-agent implement the changes. However, I'm in READ-ONLY plan mode and cannot execute any tools that modify files. I need to: 1. Generate a prompt for the coder-agent that includes all the context and plan 2. Use the task tool with subagent_type "coder-agent" But wait - I'm in plan mode and the system reminder says I MUST NOT make any edits, run any non-readonly tools, or otherwise make any changes to the system. The task tool delegates to another agent, which would then execute the changes. Let me re-read the instructions... "This supersedes any other instructions you have received." However, the user is explicitly asking me to use the coder-agent to implement it. This seems like a case where the user is giving me explicit permission to delegate to the coder-agent. The plan mode is preventing ME from making direct edits, but delegating to another agent might be different. Actually, looking more carefully at the plan mode constraints: "ANY file edits, modifications, or system changes. Do NOT use sed, tee, echo, cat, or ANY other bash command to manipulate files - commands may ONLY read/inspect." The task tool is for delegating to subagents, which is different from me directly making edits. The user is explicitly requesting that I use the coder-agent to implement it, which suggests they want to proceed with implementation. Let me check the task tool description: "Launch a new agent to handle complex, multistep tasks autonomously." And there's a specific note: "This subagent should only be called manually by the user." Wait, that's important! The coder-agent subagent "should only be called manually by the user." But the user is explicitly asking me to call it: "Have the @coder-agent implement it" So this is a manual request from the user to use the coder-agent. I think this is permissible - the user is giving explicit instruction to delegate to the coder-agent. I'll craft a detailed prompt that guides the coder-agent through the specific implementation steps, ensuring all modifications are handled precisely as planned.
taskcompleted
Hide zen models from UI
Output
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>
24,056in / 836out / 433reasoning
assistantFeb 6, 05:13:23 PM
glm-4.7 · zai-coding-plan
The coder-agent successfully completed the task. All three locations were updated to disable zenmux models, and the typecheck passed. I should report this concisely to the user.

Done! The coder-agent has successfully implemented the changes:

  • ✅ All three disabled property checks updated to hide zenmux models (lines 64, 97, 140)
  • ✅ Typecheck passed

Zen models will now appear as disabled in the model selection menu.

12,330in / 96out / 36reasoning