Files
openclaude/docs/integrations/overview.md
5a22d604f8 feat(provider): add OpenCode Zen/Go subscription support (#1350)
* feat(provider): add OpenCode Zen/Go subscription support

Add OpenCode as a first-class provider, enabling users to connect their
Zen (pay-as-you-go) and Go ($10/mo) subscriptions via the /provider command.

New integration descriptors:
- vendors/opencode.ts — OpenCode Zen vendor (41 models)
- gateways/opencode-go.ts — OpenCode Go gateway (12 models)
- brands/opencode.ts — brand descriptor
- models/opencode.ts — full model catalog (GPT, Claude, Gemini, Qwen,
  GLM, Kimi, MiniMax, Grok, DeepSeek, MiMo, Nemotron)

Modified files:
- integrationArtifacts.generated.ts — register descriptors and presets
- providerProfile.ts — add OPENCODE_API_KEY env/secret key, 'opencode'
  profile type, and buildLaunchEnv handler
- providerConfig.ts — add DEFAULT_OPENCODE_BASE_URL constants

Auth: OPENCODE_API_KEY env var or interactive key entry in /provider
Transport: openai-compatible (chat_completions)
Base URLs: https://opencode.ai/zen/v1 (Zen), /zen/go/v1 (Go)

* feat(provider): add [Zen]/[Go] tags to OpenCode preset labels

Add visual tags in the /provider preset selection to distinguish
OpenCode Zen (pay-as-you-go) from OpenCode Go (subscription).

* feat(provider): enable dynamic model discovery for OpenCode

Switch OpenCode vendor and Go gateway from static to hybrid model
catalog with openai-compatible discovery. Models are fetched from
/v1/models on startup and cached for 1 hour. Manual refresh is
supported via the /provider UI.

Static model list is preserved as fallback when discovery fails.

* test(provider): add comprehensive OpenCode Zen/Go test suite

97 tests across 2 files covering:

Integration tests (72 tests):
- Vendor descriptor: id, label, classification, base URL, model, auth,
  transport, preset, validation, catalog, discovery, usage metadata
- Gateway descriptor: id, label, vendorId, category, base URL, model,
  auth, transport, preset, catalog, discovery
- Brand descriptor: id, label, canonicalVendorId, capabilities, modelIds
- Model catalog: registration, vendor/gateway associations, required
  fields, valid classifications, reasoning/coding tags, no duplicates,
  model counts (41 Zen, 12 Go), modelDescriptorId consistency
- Cross-reference: brand↔model, vendor↔model, gateway↔model,
  shared OPENCODE_API_KEY
- Registry validation: no errors, no preset conflicts
- Edge cases: unique ids, unique apiNames, non-empty labels, valid
  contextWindow/maxOutputTokens, valid defaultModel format, validation
  message content, discovery config

Profile tests (25 tests):
- Type guard: isProviderProfile('opencode'), rejects invalid values
- buildLaunchEnv: persisted env, defaults, process env precedence,
  OPENCODE_API_KEY mapping, whitespace/null/undefined/empty handling,
  very long keys, special characters, concurrent access, boundary
  values, no credential leakage

* fix(provider): add per-model endpoint routing (P1)

Add endpointPath field to OpenAIShimTransportConfig so catalog entries
can specify which API path to use per model. This addresses the
maintainer's [P1] finding that all models were routed to
/chat/completions regardless of their upstream endpoint.

Changes:
- descriptors.ts: add endpointPath?: string to OpenAIShimTransportConfig
- openaiShim.ts: buildRequestUrl checks shimConfig.endpointPath first
- vendors/opencode.ts: add transportOverrides to 31 catalog entries
  (GPT→/responses, Claude/Qwen→/messages, Gemini→/models/<id>)
  + switch to source: 'static' to prevent free models from live API
- gateways/opencode-go.ts: add transportOverrides to 4 entries
  (MiniMax/Qwen→/messages) + switch to source: 'static'
- opencode.test.ts: update tests for static source, remove discovery tests

* refactor(opencode): model OpenCode Zen/Go as gateways (P2)

* docs(provider): document OpenCode setup and move badge metadata to descriptors

- Add OpenCode Zen/Go rows to README supported providers table
- Add OpenCode Zen/Go examples and OPENCODE_API_KEY to advanced-setup.md
- Add PresetBadge type to descriptor/manifest with badge propagation in
  artifact generator
- Move 4 hard-coded preset badges ([FREE], [Sponsor], [Zen], [Go]) from
  ProviderManager.tsx into descriptor preset metadata
- Add badge field to providerUiMetadata so UI components read from manifest
- Update integration overview docs to recommend preset.badge for future
  gateways

* fix(provider): match request body to endpoint format for OpenCode /messages and /responses (P1)

Extend the openaiShim transport so that endpointPath overrides select
both the URL and the correct body/response format:

- /responses → OpenAI Responses API body (input, max_output_tokens)
- /messages  → Anthropic Messages API body (content blocks, system, max_tokens)

Also fixes: abort listener leak in SSE passthrough, system prompt
content-block flattening, and removes [Zen]/[Go] badge entries (P3).

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>

* fix(provider): add Google AI SDK body/response format for OpenCode Zen Gemini models (P1)

The three Gemini models in the OpenCode Zen catalog (gemini-3.5-flash,
gemini-3.1-pro, gemini-3-flash) were sending chat-completions body to
the /models/gemini-* endpoint, which expects Google AI SDK format.

- effectiveTransport now detects /models/gemini- endpointPath → 'gemini'
- buildGeminiBody() converts Anthropic messages → Google contents[]
  with role mapping, systemInstruction, generationConfig, functionDeclarations
- geminiSseToAnthropic() parses Google SSE frames → Anthropic stream events
  with text deltas, functionCall tool_use, finishReason mapping
- _convertGeminiToAnthropicResponse() for non-streaming responses
- Streaming/non-streaming routing via URL detection (/models/gemini-)
- serializeBody(), hasToolsPayload, omitGeminiTools all updated

* fix: prevent OpenCode model descriptors from shadowing canonical limits

P1: Prefix all defaultModel values in opencode.ts with 'opencode-'
so the fallback findModelDescriptorForApiName() doesn't match
canonical model names. The OpenCode descriptors are still found
via catalog entry lookup when the OpenCode route is active.

P2: Add 'OpenCode Go' and 'OpenCode Zen' to PRESET_ORDER in
ProviderManager.test.tsx between 'OpenAI' and 'OpenRouter'
so navigateToPreset() sends the correct number of j keypresses.

* fix: align OpenCode Go descriptor metadata with Zen

- category: 'hosted' → 'aggregating' (both are aggregating gateways)
- add validation block with OPENCODE_API_KEY guidance
- update test assertion from 'hosted' to 'aggregating'

* fix: accept OPENAI_API_KEY as fallback in OpenCode validation

When users set up OpenCode Zen/Go via /provider, the key is saved as
OPENAI_API_KEY (via buildCompatibilityProcessEnv). The validation block
only checked OPENCODE_API_KEY, causing a startup warning even though
the runtime auth header had the key it needed.

Add OPENAI_API_KEY to validation.credentialEnvVars for both gateways,
matching the pattern used by Hicap and Gitlawb Opengateway.

* chore: trigger mergeability recheck

---------

Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
2026-05-30 14:45:08 +08:00

5.8 KiB

Integrations Overview

Purpose

This folder is the contributor-facing documentation set for the descriptor-era integration system.

Use it for:

  • terminology and architecture rules;
  • authoring rules for descriptor files;
  • how-to guides for vendors, gateways, models, anthropic proxies, and /usage;
  • reference samples that match the current implementation.

Documentation Structure

This is the current docs layout:

docs/
  architecture/
    integrations.md
  integrations/
    overview.md
    glossary.md
    how-to/
      add-vendor.md
      add-gateway.md
      add-model.md
      add-anthropic-proxy.md
      add-usage-support.md
    reference-samples.md
    common-pitfalls.md

All of the files listed above are part of the current contributor guide for the descriptor-era integration system.

Reading Order

If you are onboarding to the integration system:

  1. Read docs/architecture/integrations.md for the system boundaries.
  2. Read docs/integrations/glossary.md for the shared vocabulary.
  3. Use the how-to guides for the specific descriptor type you are adding.
  4. Use docs/integrations/reference-samples.md once the architecture and the relevant how-to guide are clear.
  5. Read docs/integrations/common-pitfalls.md before opening a docs or implementation PR for a new integration.

Core Rules

Metadata vs routing vs transport

Keep these concerns separate:

  • metadata Descriptor files declare labels, defaults, catalogs, setup requirements, validation hints, and request-shaping metadata.
  • routing Route/profile helpers map user config, presets, and env state onto the active descriptor route.
  • transport Runtime execution code actually performs the request using the active transport family.

If a change is about what a route is, it likely belongs in descriptors. If it is about how a request is executed against an external API contract, it likely belongs in transport code.

transportConfig.kind is the routing contract

For gateways, transportConfig.kind is the field that tells runtime code which transport family the route belongs to.

Examples:

  • 'openai-compatible'
  • 'local'
  • 'anthropic-proxy'
  • 'bedrock'
  • 'vertex'

Do not use gateway category for routing decisions. category is optional display/grouping metadata only.

category is descriptive, not executable

Gateway category exists to help people understand the route:

  • local
  • hosted
  • aggregating

It is valid to use category for docs, grouping, or display copy. It is not valid to treat category as the transport selector.

OpenAI-compatible request shaping belongs in openaiShim

For OpenAI-compatible or local routes, keep request-shaping metadata in transportConfig.openaiShim.

Examples:

  • maxTokensField
  • headers
  • supportsApiFormatSelection
  • supportsAuthHeaders

That matches the current runtime metadata flow in src/integrations/runtimeMetadata.ts.

supportsApiFormatSelection and supportsAuthHeaders also control the advanced /provider add and /provider edit fields for OpenAI-compatible routes. Fixed direct vendors usually set both to false; broad custom routes or gateways that intentionally accept user-supplied auth/header details set the relevant flag to true.

Descriptor Authoring Pattern

Normal descriptor files should:

  • use the define* helpers from src/integrations/define.ts;
  • default-export the descriptor object or model list;
  • keep registration out of the descriptor file;
  • keep route-owned catalogs with the route unless shared model metadata is genuinely useful;
  • put built-in model limits and capabilities in src/integrations/models/, not in env-override compatibility helpers.

Typical helper usage:

  • defineVendor
  • defineGateway
  • defineCatalog
  • defineModel
  • defineBrand
  • defineAnthropicProxy

Normal descriptor files should not:

  • call registerGateway, registerVendor, registerModel, or similar registry functions directly;
  • import registry mutation helpers just to make a descriptor visible;
  • turn simple route additions into scattered consumer edits.

Loader-Owned Registration

Registration is owned by src/integrations/index.ts.

That means the normal contributor workflow is:

  1. create or edit the descriptor file;
  2. keep the export typed through the appropriate define* helper;
  3. let the loader own registration;
  4. let registry consumers read the loaded descriptor state.

The loader may still be manually enumerated in some places today, but that is a generated-artifact concern, not a descriptor-file concern.

Normal contributor flow for new preset-participating routes is:

  1. add or edit the descriptor file;
  2. add preset metadata only when the route should be user-facing;
  3. add preset.badge metadata if the route should show a display tag (e.g. [FREE], [Sponsor]) in the preset picker — this avoids hard-coded badge logic in src/components/ProviderManager.tsx;
  4. run bun run integrations:generate;
  5. let the generated manifest feed the loader, compatibility mapping, preset typing, and provider UI metadata.

Compatibility Layer

The descriptor system is the source of truth, but a compatibility layer still exists for older env/config/public-callers.

Important compatibility surfaces include:

  • src/integrations/compatibility.ts derived legacy preset name to descriptor-route mapping;
  • src/integrations/profileResolver.ts stored provider/profile id resolution;
  • src/utils/model/providers.ts APIProvider / LegacyAPIProvider;
  • src/utils/providerFlag.ts env-facing --provider behavior.

Contributor docs should describe these as compatibility bridges, not as the primary architecture.

Preset ordering pins anthropic first, derives the middle entries from preset descriptions with standard alphanumeric sorting, and pins custom last automatically.