Skip to content

Add OpenCode provider with Go/Zen selector and session routing - #112

Open
wch wants to merge 3 commits into
mainfrom
opencode-provider
Open

Add OpenCode provider with Go/Zen selector and session routing#112
wch wants to merge 3 commits into
mainfrom
opencode-provider

Conversation

@wch

@wch wch commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Adds OpenCode support to the provider infrastructure in three parts:

  1. Custom providers pointed at OpenCode's Go and Zen endpoints now send a stable per-conversation session header and a host-supplied versioned User-Agent automatically — OpenCode rejects inference requests without them. Every request in a conversation shares the root conversation ID, a new conversation gets a new one, requests to any other provider are unchanged, and a user's explicit headers are never stripped.
  2. OpenCode becomes a built-in provider: a single opencode provider whose product setting (go or zen, default zen) resolves to the corresponding endpoint at catalog build. One workspace key works for both products, and the per-product env vars collapse to OPENCODE_API_KEY.
  3. The model-discovery cache now keys on the resolved endpoint, so switching products refetches the catalog instead of serving the previous product's list until expiry.

An explicit baseUrl still wins over the product default. Resolution also derives an effective-product projection (editable / product-enforced / base-url-override) for hosts to consume, and the configuration watch signature fires on product-only edits so live changes reach running hosts without a restart.

The companion monorepo PR consumes these changes through the updated submodule pin.

wch added 3 commits September 9, 2026 17:03
OpenCode's hosted services (Go at opencode.ai/zen/go/v1, Zen at
opencode.ai/zen/v1) reject inference requests without a stable
conversation session header and ask clients for a product User-Agent
(posit-dev/assistant#2384).

- ChatRequestMetadata (root-exported) adds optional rootConversationId
  alongside sessionId; sessionId keeps feeding the Posit AI Pass
  Session-Id header and the prompt_cache_key projection unchanged.
- New internal opencode-request-headers module privately owns the
  endpoint literals, route matching (exact host, Go/Zen roots, HTTPS
  default port), and header merging. OpenAIClient and AnthropicClient
  apply it per request on the resolved URL: the generated
  x-opencode-session wins over static custom-header workarounds, a
  user's static header is never stripped on non-matching routes, and
  nothing is generated without root metadata.
- Matching discovery requests carry the host User-Agent (never a
  session header) via a new optional createCachedModelFetcher userAgent.
- OpenAI/OpenAI-compatible/Anthropic registrars and factories (built-in
  and custom) accept an optional host User-Agent; an explicit custom
  User-Agent always wins. ProviderRegistrationConfig gains
  providerUserAgent, distinct from the Posit AI Pass userAgent so hosts
  can preserve that legacy identity.

Wire regressions (failing before implementation) cover both endpoints,
all three protocols, concurrent conversations, per-request endpoint
overrides in both directions, lookalike hosts, mixed-case header
collisions on both merge paths, and discovery.
Promote the OpenCode endpoint literals to ai-config exports
(OPENCODE_GO_BASE_URL / OPENCODE_ZEN_BASE_URL) so the catalog's
connection defaults and the bridge's header-policy matcher share one
source of truth.

- ai-config: opencode-go/opencode-zen built-in ids, openai client kind,
  base-URL connection defaults, base-only schema sections, and a
  probe-verified capability helper (2026-09-09 probes: Chat Completions
  is the only working inference route; /responses 500s, so no model
  carries an openai-responses stamp and unknown ids fall back to the
  client constructor default).
- ai-credentials: OPENCODE_GO_API_KEY / OPENCODE_ZEN_API_KEY scrubbed
  env descriptors.
- bridge: opencode-provider module (cached /models fetcher + OpenAI
  client factory with apiMode completions, both consuming the resolved
  credentials.baseUrl so configured overrides reach discovery and
  chat), registered for both ids with the host providerUserAgent.
Replace the opencode-go/opencode-zen built-in IDs with a single opencode
ID. The product choice persists as a scalar providers.opencode.product
field (go|zen, default zen) via a canonical per-provider scalar-field
schema mechanism, and resolves to the product endpoint once at catalog
build (explicit baseUrl > product selection > zen default).

ai-config owns an effective-product projection (opencodeProduct) with a
precedence-complete control state (editable | product-enforced |
base-url-override{source}) derived from per-field provenance; inertness
is authorship-based. The catalog watcher signature includes the
descriptor so product-only edits reach live hosts.

The bridge collapses to one registration and keys the discovery cache
on the resolved baseUrl so a product switch refetches the catalog.
Single credential env var: OPENCODE_API_KEY.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant