From e1f1d14117b221c664ff8ebd40993be52cc9b0f7 Mon Sep 17 00:00:00 2001 From: Matt Rubens <2600+mrubens@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:53:56 +0000 Subject: [PATCH 1/2] feat: add OpenCode Go subscription support --- SELF_HOSTING.md | 1 + .../__tests__/inference-gateway.test.ts | 19 +++++++ apps/docs/docs.json | 1 + apps/docs/environment-variables.mdx | 3 +- apps/docs/models.mdx | 3 +- apps/docs/providers/inference/opencode-go.mdx | 55 +++++++++++++++++++ apps/docs/providers/inference/opencode.mdx | 6 +- .../setup/SetupDocs.client.test.tsx | 1 + .../src/app/(onboarding)/setup/setup-docs.ts | 1 + .../trpc/commands/task-models/index.test.ts | 1 + apps/worker/src/run-task/agent-home.test.ts | 37 +++++++++++++ apps/worker/src/run-task/agent-home.ts | 55 +++++++++++++++---- deploy/compose/docker-compose.prod.yml | 1 + docker-compose.production.yml | 1 + docker-compose.self-host.yml | 1 + .../src/__tests__/inference-gateway.test.ts | 27 +++++++++ packages/types/src/inference-gateway.ts | 14 +++++ .../types/src/model-provider-config.test.ts | 36 ++++++++++++ packages/types/src/model-provider-config.ts | 38 ++++++++++++- packages/types/src/task-models.test.ts | 3 + packages/types/src/task-models.ts | 1 + 21 files changed, 289 insertions(+), 16 deletions(-) create mode 100644 apps/docs/providers/inference/opencode-go.mdx diff --git a/SELF_HOSTING.md b/SELF_HOSTING.md index 9d49a38d1..d24437bb9 100644 --- a/SELF_HOSTING.md +++ b/SELF_HOSTING.md @@ -404,6 +404,7 @@ common provider keys into worker containers: - `ZAI_CODING_PLAN_API_KEY` (Z.AI Coding Plan, `zai-coding-plan/...` models, with `ZAI_CODING_PLAN_REGION`) - `OPENCODE_API_KEY` +- `OPENCODE_GO_API_KEY` (OpenCode Go subscription, `opencode-go/...` models) If your provider uses another env var name, list it in `R_MODEL_ENV_KEYS`: diff --git a/apps/api/src/handlers/inference/__tests__/inference-gateway.test.ts b/apps/api/src/handlers/inference/__tests__/inference-gateway.test.ts index e913f057d..fc9a5f6b2 100644 --- a/apps/api/src/handlers/inference/__tests__/inference-gateway.test.ts +++ b/apps/api/src/handlers/inference/__tests__/inference-gateway.test.ts @@ -1187,6 +1187,25 @@ describe('inference gateway', () => { expect(headers.get('authorization')).toBe('Bearer provider-secret-key'); }); + it('forwards OpenCode Go requests to the subscription endpoint', async () => { + const fetchMock = stubUpstreamFetch(); + const response = await postMessages( + createApp(createRunToken()), + '/api/inference/opencode-go/v1/responses', + ); + + expect(response.status).toBe(200); + expect(mockResolveModelProviderEnvValue).toHaveBeenCalledWith([ + 'OPENCODE_GO_API_KEY', + ]); + + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(url).toBe('https://opencode.ai/zen/go/v1/responses'); + + const headers = new Headers(init.headers); + expect(headers.get('authorization')).toBe('Bearer provider-secret-key'); + }); + it('preserves the query string on upstream requests', async () => { const fetchMock = stubUpstreamFetch(); const app = createApp(createRunToken()); diff --git a/apps/docs/docs.json b/apps/docs/docs.json index c369d0646..71155bbe9 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -77,6 +77,7 @@ "providers/inference/openai", "providers/inference/openai-compatible", "providers/inference/opencode", + "providers/inference/opencode-go", "providers/inference/openrouter", "providers/inference/together-ai", "providers/inference/vercel-ai-gateway", diff --git a/apps/docs/environment-variables.mdx b/apps/docs/environment-variables.mdx index 2f540d8d5..7894de280 100644 --- a/apps/docs/environment-variables.mdx +++ b/apps/docs/environment-variables.mdx @@ -201,7 +201,8 @@ as per-task auth tokens or workspace paths. | `ZAI_REGION` | Provider key | Z.AI region: `global` (International) or `china`. Defaults to `global` when unset. | | `ZAI_CODING_PLAN_API_KEY` | Provider key | Z.AI Coding Plan API key for `zai-coding-plan/...` models. Not a general platform key. | | `ZAI_CODING_PLAN_REGION` | Provider key | Z.AI Coding Plan region: `global` (International) or `china`. Defaults to `global` when unset. | -| `OPENCODE_API_KEY` | Provider key | OpenCode Zen / Go API key. | +| `OPENCODE_API_KEY` | Provider key | OpenCode Zen API key. | +| `OPENCODE_GO_API_KEY` | Provider key | OpenCode API key for an active Go subscription and `opencode-go/...` models. | | `GEMINI_API_KEY` | Provider key | Google Gemini API key. Can also be saved from **Settings > Models**. | | `GOOGLE_GENERATIVE_AI_API_KEY` | Provider key | Alternate Google/Gemini provider key forwarded when configured or inferred. | | `AWS_BEARER_TOKEN_BEDROCK` | Provider key | Amazon Bedrock Mantle API key. Can also be saved from **Settings > Models**. | diff --git a/apps/docs/models.mdx b/apps/docs/models.mdx index 959f84f64..c716bb9e9 100644 --- a/apps/docs/models.mdx +++ b/apps/docs/models.mdx @@ -51,7 +51,7 @@ These connections use metered API billing or a provider-managed gateway: | [MiniMax](/providers/inference/minimax) | MiniMax API key | MiniMax account | | [Moonshot AI (Kimi)](/providers/inference/moonshot-ai) | Kimi Open Platform key | Moonshot Open Platform balance | | [OpenAI](/providers/inference/openai) | OpenAI API key | OpenAI API project | -| [OpenCode Zen / Go](/providers/inference/opencode) | OpenCode API key | OpenCode account | +| [OpenCode Zen](/providers/inference/opencode) | OpenCode API key | OpenCode account | | [OpenRouter](/providers/inference/openrouter) | OpenRouter API key | OpenRouter balance and key limit | | [Together AI](/providers/inference/together-ai) | Together AI API key | Together AI account | | [Vercel AI Gateway](/providers/inference/vercel-ai-gateway) | AI Gateway API key | Vercel team | @@ -68,6 +68,7 @@ general API balance: | [ChatGPT Subscription](/providers/inference/chatgpt) | OpenAI device authorization | ChatGPT plan windows | | [GitHub Copilot](/providers/inference/github-copilot) | GitHub device authorization | Copilot plan allowance | | [Kimi for Coding](/providers/inference/kimi-for-coding) | Kimi membership key | Coding membership allowance | +| [OpenCode Go](/providers/inference/opencode-go) | OpenCode API key | Go plan allowance | | [xAI Grok Subscription](/providers/inference/xai-subscription) | xAI account authorization | SuperGrok or eligible X plan allowance | | [Z.AI Coding Plan](/providers/inference/zai-coding-plan) | Regional Coding Plan key | Coding Plan quota windows | diff --git a/apps/docs/providers/inference/opencode-go.mdx b/apps/docs/providers/inference/opencode-go.mdx new file mode 100644 index 000000000..3e0dd18f6 --- /dev/null +++ b/apps/docs/providers/inference/opencode-go.mdx @@ -0,0 +1,55 @@ +--- +title: OpenCode Go +icon: 'https://unpkg.com/@lobehub/icons-static-svg@1.94.0/icons/opencode.svg' +description: Use an OpenCode Go subscription with Roomote. +--- + +OpenCode Go is a subscription for a curated catalog of coding models. Use this +provider when you want Roomote tasks to consume the allowance included with +your Go plan. + +## Subscribe and get an API key + +Sign in to [OpenCode](https://opencode.ai/auth), subscribe to Go, and copy your +API key. OpenCode currently allows one Go subscription per workspace. + +## Configuration + +Add **OpenCode Go** in **Settings > Models** and paste the key, or set: + +```sh +OPENCODE_GO_API_KEY=... +``` + +Roomote uses a separate environment variable so Zen and Go can be configured +independently. Paste the OpenCode API key issued for the workspace; Roomote +binds it to OpenCode's `opencode-go` provider at runtime. + +Go models use the `opencode-go/` prefix. Connecting the provider adds a curated +set of eligible models and recommends lower-cost options for helper work. + +## Usage behavior + +Requests consume the rolling and monthly allowances defined by your Go plan. +The OpenCode account console is authoritative for current usage and resets. If +you enable **Use balance** in OpenCode, requests can fall back to your Zen +balance after the included allowance is exhausted. + +See [OpenCode Go documentation](https://opencode.ai/docs/go/) for current +models, limits, pricing, and retention details. + +## Verify setup + +1. save `OPENCODE_GO_API_KEY` +2. confirm OpenCode Go models appear in **Settings > Models** +3. apply a preset or assign an `opencode-go/...` model manually +4. run a small task and confirm it appears in OpenCode usage + +## Common issues + +- **The key is rejected.** Confirm the workspace has an active Go subscription + and that you copied its OpenCode API key. +- **A model is unavailable.** Go's catalog can change; select another model + shown in the OpenCode Go console. +- **Requests stop after sustained use.** Check the plan's rolling, weekly, and + monthly allowance windows in OpenCode. diff --git a/apps/docs/providers/inference/opencode.mdx b/apps/docs/providers/inference/opencode.mdx index cf8b8093c..f3c9685f1 100644 --- a/apps/docs/providers/inference/opencode.mdx +++ b/apps/docs/providers/inference/opencode.mdx @@ -1,5 +1,5 @@ --- -title: OpenCode Zen / Go +title: OpenCode Zen icon: 'https://unpkg.com/@lobehub/icons-static-svg@1.94.0/icons/opencode.svg' description: Use OpenCode's routed Zen model service with Roomote. --- @@ -16,13 +16,13 @@ selected models. ## Configuration -Add **OpenCode Zen / Go** in **Settings > Models** and paste the key, or set: +Add **OpenCode Zen** in **Settings > Models** and paste the key, or set: ```sh OPENCODE_API_KEY=... ``` -Roomote exposes routed models under the `opencode/` prefix and adds a recommended +Roomote exposes routed Zen models under the `opencode/` prefix and adds a recommended set after connection. The preset can use inexpensive routed models for helper work and stronger models for coding, planning, vision, and review. diff --git a/apps/web/src/app/(onboarding)/setup/SetupDocs.client.test.tsx b/apps/web/src/app/(onboarding)/setup/SetupDocs.client.test.tsx index fbcc143e6..6ef8b7678 100644 --- a/apps/web/src/app/(onboarding)/setup/SetupDocs.client.test.tsx +++ b/apps/web/src/app/(onboarding)/setup/SetupDocs.client.test.tsx @@ -52,6 +52,7 @@ describe('SetupDocs', () => { ['openai', 'openai'], ['openai-compatible', 'openai-compatible'], ['opencode', 'opencode'], + ['opencode-go', 'opencode-go'], ['openrouter', 'openrouter'], ['togetherai', 'together-ai'], ['vercel', 'vercel-ai-gateway'], diff --git a/apps/web/src/app/(onboarding)/setup/setup-docs.ts b/apps/web/src/app/(onboarding)/setup/setup-docs.ts index acba0809f..fa9db79aa 100644 --- a/apps/web/src/app/(onboarding)/setup/setup-docs.ts +++ b/apps/web/src/app/(onboarding)/setup/setup-docs.ts @@ -66,6 +66,7 @@ const MODEL_PROVIDER_DOC_PATHS: Partial< openai: 'providers/inference/openai', 'openai-compatible': 'providers/inference/openai-compatible', opencode: 'providers/inference/opencode', + 'opencode-go': 'providers/inference/opencode-go', openrouter: 'providers/inference/openrouter', togetherai: 'providers/inference/together-ai', vercel: 'providers/inference/vercel-ai-gateway', diff --git a/apps/web/src/trpc/commands/task-models/index.test.ts b/apps/web/src/trpc/commands/task-models/index.test.ts index 8f7e86542..4fb718a3c 100644 --- a/apps/web/src/trpc/commands/task-models/index.test.ts +++ b/apps/web/src/trpc/commands/task-models/index.test.ts @@ -97,6 +97,7 @@ const PROVIDER_ENV_VAR_NAMES = [ 'ZAI_CODING_PLAN_API_KEY', 'ZAI_CODING_PLAN_REGION', 'OPENCODE_API_KEY', + 'OPENCODE_GO_API_KEY', 'AWS_BEARER_TOKEN_BEDROCK', 'AWS_REGION', 'GEMINI_API_KEY', diff --git a/apps/worker/src/run-task/agent-home.test.ts b/apps/worker/src/run-task/agent-home.test.ts index a8aa6a66e..38ffaac03 100644 --- a/apps/worker/src/run-task/agent-home.test.ts +++ b/apps/worker/src/run-task/agent-home.test.ts @@ -316,6 +316,43 @@ describe('generateOpenCodeConfig provider support', () => { }); }); + it('binds OpenCode Go to its dedicated key in direct mode', () => { + const result = generateOpenCodeConfig({ + homeDir: createHomeDir(), + runtimeEnv: { + R_MODEL: 'opencode-go/kimi-k2.7-code', + OPENCODE_GO_API_KEY: 'go-key', + }, + }); + const config = JSON.parse(result.configContent) as { + provider: Record }>; + }; + + expect(config.provider['opencode-go']?.options).toMatchObject({ + apiKey: '{env:OPENCODE_GO_API_KEY}', + }); + expect(result.configContent).not.toContain('go-key'); + }); + + it('rebases OpenCode Go onto its inference gateway route', () => { + const result = generateOpenCodeConfig({ + homeDir: createHomeDir(), + runtimeEnv: { + R_MODEL: 'opencode-go/kimi-k2.7-code', + R_INFERENCE_GATEWAY_URL: 'https://api.example.com/api/inference', + R_INFERENCE_GATEWAY_KEYS: 'OPENCODE_GO_API_KEY', + }, + }); + const config = JSON.parse(result.configContent) as { + provider: Record }>; + }; + + expect(config.provider['opencode-go']?.options).toMatchObject({ + baseURL: 'https://api.example.com/api/inference/opencode-go/v1', + apiKey: '{env:ROOMOTE_CLOUD_TOKEN}', + }); + }); + it('rebases Azure providers onto the inference gateway without a /v1 suffix', () => { const result = generateOpenCodeConfig({ homeDir: createHomeDir(), diff --git a/apps/worker/src/run-task/agent-home.ts b/apps/worker/src/run-task/agent-home.ts index 4f7efb38b..8a7a0855d 100644 --- a/apps/worker/src/run-task/agent-home.ts +++ b/apps/worker/src/run-task/agent-home.ts @@ -38,6 +38,7 @@ import { resolveOpenRouterVariantModelAlias, OPENCODE_ARCHITECT_AGENT, OPENCODE_AUTH_CONTENT_ENV_VAR_NAME, + OPENCODE_GO_API_KEY_ENV_VAR_NAME, TaskPayloadKind, type EnvironmentManualSkill, type OpenRouterVariantModelAlias, @@ -1095,6 +1096,37 @@ function mergeAzureCognitiveServicesProviderConfig( }; } +/** + * OpenCode's catalog maps both Zen and Go to OPENCODE_API_KEY. Roomote keeps + * their credentials separate, so bind Go to its dedicated env var directly. + * Gateway mode replaces this value with the run token later. + */ +function mergeOpenCodeGoProviderConfig( + providerConfig: Record, + modelIds: Array, +): Record { + const providerId = 'opencode-go'; + if ( + !modelIds.some((modelId) => modelId?.trim().startsWith(`${providerId}/`)) + ) { + return providerConfig; + } + + const existingProvider = asRecord(providerConfig[providerId]); + const existingOptions = asRecord(existingProvider.options); + + return { + ...providerConfig, + [providerId]: { + ...existingProvider, + options: { + apiKey: `{env:${OPENCODE_GO_API_KEY_ENV_VAR_NAME}}`, + ...existingOptions, + }, + }, + }; +} + function normalizeStringList(value: unknown): string[] { if (Array.isArray(value)) { return value.filter((entry): entry is string => typeof entry === 'string'); @@ -1703,18 +1735,22 @@ function resolveModelBackedOpenCodeConfig( ) : providerReasoningConfig; const providerConfig = mergeInferenceGatewayProviderConfig( - mergeAzureCognitiveServicesProviderConfig( - mergeAmazonBedrockProviderConfig( - mergeBedrockMantleProviderConfig( - mergeBedrockMantleOpenAiProviderConfig( - mergeOpenAiCompatibleProviderConfig( - mergeOpenRouterVariantAliasModels( - providerModelConfig, - variantAliases, + mergeOpenCodeGoProviderConfig( + mergeAzureCognitiveServicesProviderConfig( + mergeAmazonBedrockProviderConfig( + mergeBedrockMantleProviderConfig( + mergeBedrockMantleOpenAiProviderConfig( + mergeOpenAiCompatibleProviderConfig( + mergeOpenRouterVariantAliasModels( + providerModelConfig, + variantAliases, + ), + runtimeEnv, + configuredModelIds, + visionModel ?? effectiveCodingModel, ), runtimeEnv, configuredModelIds, - visionModel ?? effectiveCodingModel, ), runtimeEnv, configuredModelIds, @@ -1722,7 +1758,6 @@ function resolveModelBackedOpenCodeConfig( runtimeEnv, configuredModelIds, ), - runtimeEnv, configuredModelIds, ), configuredModelIds, diff --git a/deploy/compose/docker-compose.prod.yml b/deploy/compose/docker-compose.prod.yml index cf0f9f75c..e89ad8164 100644 --- a/deploy/compose/docker-compose.prod.yml +++ b/deploy/compose/docker-compose.prod.yml @@ -64,6 +64,7 @@ x-roomote-inference-env: &roomote-inference-env ZAI_CODING_PLAN_API_KEY: ${ZAI_CODING_PLAN_API_KEY:-} ZAI_CODING_PLAN_REGION: ${ZAI_CODING_PLAN_REGION:-} OPENCODE_API_KEY: ${OPENCODE_API_KEY:-} + OPENCODE_GO_API_KEY: ${OPENCODE_GO_API_KEY:-} BASETEN_API_KEY: ${BASETEN_API_KEY:-} TOGETHER_API_KEY: ${TOGETHER_API_KEY:-} diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 994a91aa9..06300ff1c 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -52,6 +52,7 @@ x-roomote-production-env: &roomote-production-env ZAI_CODING_PLAN_API_KEY: ${ZAI_CODING_PLAN_API_KEY:-} ZAI_CODING_PLAN_REGION: ${ZAI_CODING_PLAN_REGION:-} OPENCODE_API_KEY: ${OPENCODE_API_KEY:-} + OPENCODE_GO_API_KEY: ${OPENCODE_GO_API_KEY:-} BASETEN_API_KEY: ${BASETEN_API_KEY:-} TOGETHER_API_KEY: ${TOGETHER_API_KEY:-} GITHUB_TOKEN: ${GITHUB_TOKEN:-} diff --git a/docker-compose.self-host.yml b/docker-compose.self-host.yml index b2f30730e..449b6e265 100644 --- a/docker-compose.self-host.yml +++ b/docker-compose.self-host.yml @@ -50,6 +50,7 @@ x-roomote-env: &roomote-env ZAI_CODING_PLAN_API_KEY: ${ZAI_CODING_PLAN_API_KEY:-} ZAI_CODING_PLAN_REGION: ${ZAI_CODING_PLAN_REGION:-} OPENCODE_API_KEY: ${OPENCODE_API_KEY:-} + OPENCODE_GO_API_KEY: ${OPENCODE_GO_API_KEY:-} BASETEN_API_KEY: ${BASETEN_API_KEY:-} TOGETHER_API_KEY: ${TOGETHER_API_KEY:-} GITHUB_TOKEN: ${GITHUB_TOKEN:-} diff --git a/packages/types/src/__tests__/inference-gateway.test.ts b/packages/types/src/__tests__/inference-gateway.test.ts index 70ef8dfc9..0798e555e 100644 --- a/packages/types/src/__tests__/inference-gateway.test.ts +++ b/packages/types/src/__tests__/inference-gateway.test.ts @@ -54,6 +54,33 @@ describe('inference gateway URL builders', () => { ); }); + it('registers OpenCode Go with its subscription endpoint and API key', () => { + const provider = getInferenceGatewayProvider('opencode-go'); + + expect(provider).toMatchObject({ + envVarNames: ['OPENCODE_GO_API_KEY'], + upstreamBaseUrl: 'https://opencode.ai/zen/go', + authHeader: { name: 'authorization', scheme: 'bearer' }, + openCodeBaseUrlSuffix: '/v1', + }); + expect(provider?.allowedPaths).toEqual( + expect.arrayContaining([ + '/v1/chat/completions', + '/v1/responses', + '/v1/messages', + ]), + ); + expect( + buildInferenceGatewayOpenCodeBaseUrl( + 'https://api.example.com/api/inference', + provider!, + ), + ).toBe('https://api.example.com/api/inference/opencode-go/v1'); + expect( + getInferenceGatewayProviderByEnvVarName('OPENCODE_GO_API_KEY')?.id, + ).toBe('opencode-go'); + }); + it('registers native Amazon Bedrock with its regional runtime endpoint', () => { const provider = getInferenceGatewayProvider('amazon-bedrock'); diff --git a/packages/types/src/inference-gateway.ts b/packages/types/src/inference-gateway.ts index 1ab236900..c20812be7 100644 --- a/packages/types/src/inference-gateway.ts +++ b/packages/types/src/inference-gateway.ts @@ -1,4 +1,5 @@ import type { SetupModelProviderId } from './model-provider-config'; +import { OPENCODE_GO_API_KEY_ENV_VAR_NAME } from './model-provider-config'; import { getOpenAiCompatibleProviderInstance, isOpenAiCompatibleProviderEnvVarName, @@ -379,6 +380,19 @@ export const INFERENCE_GATEWAY_PROVIDERS: readonly InferenceGatewayProvider[] = allowedPaths: OPENAI_COMPATIBLE_INFERENCE_PATHS, openCodeBaseUrlSuffix: '/v1', }, + { + id: 'opencode-go', + name: 'OpenCode Go', + envVarNames: [OPENCODE_GO_API_KEY_ENV_VAR_NAME], + upstreamBaseUrl: 'https://opencode.ai/zen/go', + authHeader: { name: 'authorization', scheme: 'bearer' }, + allowedPaths: [ + ...OPENAI_COMPATIBLE_INFERENCE_PATHS, + ...OPENAI_RESPONSES_INFERENCE_PATHS, + ...ANTHROPIC_COMPATIBLE_INFERENCE_PATHS, + ], + openCodeBaseUrlSuffix: '/v1', + }, { id: 'xai', name: 'xAI', diff --git a/packages/types/src/model-provider-config.test.ts b/packages/types/src/model-provider-config.test.ts index 4901be47a..3a1414dc4 100644 --- a/packages/types/src/model-provider-config.test.ts +++ b/packages/types/src/model-provider-config.test.ts @@ -251,6 +251,7 @@ describe('SETUP_MODEL_PROVIDER_CATALOG', () => { 'kimi-for-coding', 'minimax', 'opencode', + 'opencode-go', 'amazon-bedrock', 'google', 'xai', @@ -351,6 +352,7 @@ describe('SETUP_MODEL_PROVIDER_CATALOG', () => { { providerId: 'moonshotai', modelId: 'moonshotai/kimi-k3' }, { providerId: 'kimi-for-coding', modelId: 'kimi-for-coding/k3' }, { providerId: 'opencode', modelId: 'opencode/kimi-k3' }, + { providerId: 'opencode-go', modelId: 'opencode-go/kimi-k3' }, ]); }); @@ -368,9 +370,35 @@ describe('SETUP_MODEL_PROVIDER_CATALOG', () => { expect(providersByModel).toEqual([ { providerId: 'openrouter', modelId: 'openrouter/qwen/qwen3.8-max' }, { providerId: 'vercel', modelId: 'vercel/alibaba/qwen3.8-max' }, + { providerId: 'opencode-go', modelId: 'opencode-go/qwen3.8-max' }, ]); }); + it('maps the central DeepSeek V4 Flash recommendation to the Go stable alias', () => { + const provider = getSetupModelProvider('opencode-go'); + + expect(provider.suggestedTaskModels).toContainEqual({ + id: 'opencode-go/deepseek-v4-flash', + displayName: 'DeepSeek V4 Flash 0731', + family: 'DeepSeek', + }); + }); + + it('uses the requested OpenCode Go role defaults', () => { + expect( + buildRecommendedDeploymentModelConfig( + getSetupModelProvider('opencode-go'), + ), + ).toMatchObject({ + roomoteModel: 'opencode-go/glm-5.2', + roomoteSmallModel: 'opencode-go/gpt-5.6-luna', + roomoteVisionModel: 'opencode-go/gpt-5.6-luna', + roomoteCodeReviewModel: 'opencode-go/minimax-m3', + roomoteExploreModel: 'opencode-go/deepseek-v4-flash', + roomotePlanningModel: 'opencode-go/qwen3.8-max', + }); + }); + it.each([ { displayName: 'GPT 5.6 Sol', @@ -407,6 +435,14 @@ describe('SETUP_MODEL_PROVIDER_CATALOG', () => { modelId: `azure-cognitive-services/${modelId}`, }, { providerId: 'opencode', modelId: `opencode/${modelId}` }, + ...(modelId === 'gpt-5.6-luna' + ? [ + { + providerId: 'opencode-go', + modelId: 'opencode-go/gpt-5.6-luna', + }, + ] + : []), { providerId: 'amazon-bedrock', modelId: `bedrock-mantle/openai.${modelId}`, diff --git a/packages/types/src/model-provider-config.ts b/packages/types/src/model-provider-config.ts index 2a31b5061..047e5b671 100644 --- a/packages/types/src/model-provider-config.ts +++ b/packages/types/src/model-provider-config.ts @@ -43,6 +43,9 @@ export const CHATGPT_SUBSCRIPTION_PROVIDER_ID = 'chatgpt' as const; */ export const XAI_SUBSCRIPTION_PROVIDER_ID = 'xai-subscription' as const; +/** Roomote-specific key name for OpenCode Go, kept separate from Zen. */ +export const OPENCODE_GO_API_KEY_ENV_VAR_NAME = 'OPENCODE_GO_API_KEY' as const; + export const SETUP_MODEL_PROVIDER_IDS = [ 'openrouter', ...ENABLED_DIRECT_TASK_MODEL_PROVIDER_IDS, @@ -564,7 +567,7 @@ export const SETUP_MODEL_PROVIDER_CATALOG = [ }, { id: 'opencode', - label: 'OpenCode Zen / Go', + label: 'OpenCode Zen', envVarName: 'OPENCODE_API_KEY', defaultRoomoteModel: 'opencode/big-pickle', authKind: 'api-key', @@ -599,6 +602,39 @@ export const SETUP_MODEL_PROVIDER_CATALOG = [ }, recommendedRoleReasoningEfforts: { codeReview: 'medium' }, }, + { + id: 'opencode-go', + label: 'OpenCode Go', + envVarName: OPENCODE_GO_API_KEY_ENV_VAR_NAME, + credentialHelp: { + text: 'Subscribe to OpenCode Go and copy the API key from your OpenCode account.', + href: 'https://opencode.ai/auth', + linkLabel: 'Open OpenCode account', + }, + defaultRoomoteModel: 'opencode-go/glm-5.2', + authKind: 'api-key', + // Go serves a broader catalog; only models in Roomote's central curated + // recommendation list are suggested here. + suggestedTaskModels: mapRecommendedTaskModels({ + 'deepseek-v4-flash-0731': 'opencode-go/deepseek-v4-flash', + 'deepseek-v4-pro': 'opencode-go/deepseek-v4-pro', + 'glm-5-2': 'opencode-go/glm-5.2', + 'gpt-5-6-luna': 'opencode-go/gpt-5.6-luna', + 'grok-4-5': 'opencode-go/grok-4.5', + 'kimi-k2-7-code': 'opencode-go/kimi-k2.7-code', + 'kimi-k3': 'opencode-go/kimi-k3', + 'mimo-v2-5': 'opencode-go/mimo-v2.5', + 'minimax-m3': 'opencode-go/minimax-m3', + 'qwen3-8-max': 'opencode-go/qwen3.8-max', + }), + recommendedRoleModels: { + helper: 'opencode-go/gpt-5.6-luna', + vision: 'opencode-go/gpt-5.6-luna', + codeReview: 'opencode-go/minimax-m3', + explore: 'opencode-go/deepseek-v4-flash', + planning: 'opencode-go/qwen3.8-max', + }, + }, { // Bedrock's current console issues API keys for the Mantle endpoint. The // worker registers this as a custom Anthropic-compatible OpenCode provider diff --git a/packages/types/src/task-models.test.ts b/packages/types/src/task-models.test.ts index 6f4a98e54..98030c9ec 100644 --- a/packages/types/src/task-models.test.ts +++ b/packages/types/src/task-models.test.ts @@ -73,6 +73,9 @@ describe('normalizeTaskModelId', () => { expect(normalizeTaskModelId('opencode/big-pickle')).toBe( 'opencode/big-pickle', ); + expect(normalizeTaskModelId('opencode-go/kimi-k2.7-code')).toBe( + 'opencode-go/kimi-k2.7-code', + ); expect( normalizeTaskModelId('bedrock-mantle/anthropic.claude-sonnet-5'), ).toBe('bedrock-mantle/anthropic.claude-sonnet-5'); diff --git a/packages/types/src/task-models.ts b/packages/types/src/task-models.ts index 4951f2c1b..ec92b5ae7 100644 --- a/packages/types/src/task-models.ts +++ b/packages/types/src/task-models.ts @@ -29,6 +29,7 @@ export const ENABLED_DIRECT_TASK_MODEL_PROVIDER_IDS = [ 'kimi-for-coding', 'minimax', 'opencode', + 'opencode-go', 'amazon-bedrock', 'google', 'xai', From b69076846096db17af2de00665fa081eac847207 Mon Sep 17 00:00:00 2001 From: Matt Rubens <2600+mrubens@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:55:17 +0000 Subject: [PATCH 2/2] test: cover OpenCode Go DeepSeek alias --- packages/types/src/model-provider-config.test.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/types/src/model-provider-config.test.ts b/packages/types/src/model-provider-config.test.ts index 3a1414dc4..a33e6bbf2 100644 --- a/packages/types/src/model-provider-config.test.ts +++ b/packages/types/src/model-provider-config.test.ts @@ -374,16 +374,6 @@ describe('SETUP_MODEL_PROVIDER_CATALOG', () => { ]); }); - it('maps the central DeepSeek V4 Flash recommendation to the Go stable alias', () => { - const provider = getSetupModelProvider('opencode-go'); - - expect(provider.suggestedTaskModels).toContainEqual({ - id: 'opencode-go/deepseek-v4-flash', - displayName: 'DeepSeek V4 Flash 0731', - family: 'DeepSeek', - }); - }); - it('uses the requested OpenCode Go role defaults', () => { expect( buildRecommendedDeploymentModelConfig( @@ -503,6 +493,10 @@ describe('SETUP_MODEL_PROVIDER_CATALOG', () => { providerId: 'opencode', modelId: 'opencode/deepseek-v4-flash', }, + { + providerId: 'opencode-go', + modelId: 'opencode-go/deepseek-v4-flash', + }, ]); });