Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
fac027b
fix: treat empty SDK endpoints as unspecified defaults
BitL8-ByteShort Sep 8, 2026
ef07545
feat: add connection snapshots and v4 paidEligibility migration
BitL8-ByteShort Sep 8, 2026
55901ca
feat: centralize eligibility for configured-connection paid access
BitL8-ByteShort Sep 8, 2026
87be12d
fix: accept provider-owned fetch transports on paid routes
BitL8-ByteShort Sep 8, 2026
517d878
feat: connection-aware usage, panel copy, and 0.4.0 readiness
BitL8-ByteShort Sep 9, 2026
a1b63dc
fix: retain connection binding on repair and update acceptance fixtures
BitL8-ByteShort Sep 9, 2026
0fb98bf
test: accept Free-policy access copy in browser panel
BitL8-ByteShort Sep 9, 2026
664f947
docs: record host, browser, and metadata acceptance evidence
BitL8-ByteShort Sep 9, 2026
433a105
fix: wire connections, complete attribution, and prove subscription f…
BitL8-ByteShort Sep 9, 2026
bd1d96c
fix: keep revoked connections, prove fetch transport, flush attribution
BitL8-ByteShort Sep 9, 2026
26c4dd9
fix: use auth-loader fetch and keep accounting off the repair path
BitL8-ByteShort Sep 9, 2026
b2f9791
Complete connection billing controls and release acceptance
BitL8-ByteShort Sep 9, 2026
764eeae
Bind acceptance role edits to the current connection revision
BitL8-ByteShort Sep 9, 2026
3ef4eda
Preserve a valid disabled choice in the legacy upgrade fixture
BitL8-ByteShort Sep 9, 2026
33403f4
Validate legacy upgrades against their actual saved policy
BitL8-ByteShort Sep 9, 2026
060320f
Guard mixed provider route bindings before release
BitL8-ByteShort Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

All notable changes to OpenCode Model Control are recorded here. The project follows [Semantic Versioning](https://semver.org/).

## 0.4.0

- Treat empty SDK-default endpoints as unspecified rather than invalid, without letting a missing public URL certify a custom gateway.
- Accept OpenCode provider-owned authentication transports on Paid routes when the exact model binding matches. Do not inspect transport source or fall back to another billing connection.
- Add connection snapshots and settings schema 4. Existing Paid installs migrate to `verified-pricing`; selecting the new Paid control adopts `configured-connections`.
- Free routing still requires verified free prices. Configured Paid access can use a host route when public estimates are unavailable.
- Report tokens separately from OpenCode-recorded cost. Missing cost stays unreported. Quota may be not reported. Historical usage is not relabelled after a login change.
- Show configured connection billing declarations, evidence sources, exact role bindings, and separate captured usage by billing kind and currency. Connection edits require current revisions; drafts survive conflicts.
- Reload connection evidence in planner and MCP routes. Bound attribution work and retention, preserve assistant-message history and price snapshots, and flush on host disposal.
- Verify upgrades from the exact public 0.3.0 package under both Free and legacy Paid policy, including private backups and guarded update/recovery.
- Bind mixed-endpoint providers to their per-model route mapping, including endpoint/SDK swaps. Changed connections invalidate pins and retained repairs and discard stale quota.
- Managed integration surface version 3; an explicit Connect/update and OpenCode restart is required for plugin behavior changes.

## 0.3.0

- Replace historical free-model authorization with exact provider/model/API pricing evidence from credential-free public Models.dev metadata, including all supported supplied billing dimensions. Refresh every 15 minutes while active; expire evidence after 24 hours and block missing, conflicting, malformed, or expired pricing at dispatch.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Documentation-only changes should still run `npm run verify` when practical. Sta
4. Update documentation when behavior, support, security, or benchmark claims change.
5. Run `npm run verify` and report any check you could not run.

Routing changes must keep unknown/expired pricing blocked and preserve the verified-free default. Saving Paid is the explicit authorization for known-paid routing; with auto-include on, it also authorizes future eligible known-paid models without a per-model click. Preserve explicit disables and never write inferred enrollment as saved intent. Public metadata cannot expand host-effective capabilities. Preserve existing ranking and defer authentication integrations unless separately approved. Connector changes must preserve unrelated OpenCode configuration, fail closed on ownership conflicts, and include isolated install/disconnect tests.
Routing changes must preserve the verified-free default. Free and migrated `verified-pricing` Paid still block unknown/expired public prices. After a user saves the new Paid control (`configured-connections`), a configured host route may be eligible without a public estimate; malformed endpoints and changed bindings stay blocked. Preserve explicit disables and never write inferred enrollment as saved intent. Public metadata cannot expand host-effective capabilities or certify a custom endpoint. Preserve existing ranking and defer authentication integrations unless separately approved. Connector changes must preserve unrelated OpenCode configuration, fail closed on ownership conflicts, and include isolated install/disconnect tests.

Do not include credentials, private prompts, user transcripts, proprietary source code, benchmark data you cannot redistribute, or code copied from closed-source routers. Contributions must be clean-room work or compatible third-party material with its provenance and license recorded.

Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The control panel runs on `127.0.0.1`. OpenCode remains responsible for provider

The running app is authoritative for model names, availability, pricing evidence, and role eligibility.

> This source documents **0.3.0**; `@latest` installs the version currently published on [npm](https://www.npmjs.com/package/opencode-model-control). Check the [release index](https://github.com/BitL8-ByteShort/opencode-model-control/releases) for availability and the [support matrix](docs/support-matrix.md) for verified compatibility.
> This source documents **0.4.0**; `@latest` installs the version currently published on [npm](https://www.npmjs.com/package/opencode-model-control). Check the [release index](https://github.com/BitL8-ByteShort/opencode-model-control/releases) for availability and the [support matrix](docs/support-matrix.md) for verified compatibility.

## What it does

Expand All @@ -17,8 +17,8 @@ The running app is authoritative for model names, availability, pricing evidence
- Separates discovery, saved inclusion intent, and effective eligibility. Auto-include defaults on and follows the saved cost policy; explicit disables remain authoritative.
- Provides a **Free / Paid** preference:
- **Free** permits only independently verified zero-cost models.
- **Paid** permits verified free and known paid models, prioritizing paid models for compatible automatic assignments.
- Unknown or ambiguous pricing is always shown as **Unknown — blocked**.
- **Paid** permits eligible configured OpenCode connections, including subscription and API access when public prices are unavailable. Existing Paid settings retain verified-price behavior until you explicitly adopt configured Paid access.
- Unknown prices remain unreported. They block Free and legacy verified-price Paid routing, but do not by themselves block configured Paid access.
- Resolves the orchestrator, code, vision, and review roles from current eligible models without a fixed free-model roster or new quality ranking.
- Transparently sends media-only analysis through the saved compatible, tool-free vision worker. Only explicit user-authored text classified as a code change keeps Omc-Router active for a seamless vision-to-code-to-review workflow.
- Automatically routes approved code changes through a code worker, an independent reviewer, and at most one repair pass without requiring `@` mentions.
Expand Down Expand Up @@ -108,9 +108,9 @@ The connector writes absolute Node and package CLI paths, so a source checkout d

### Direct GitHub release artifact

The [GitHub release index](https://github.com/BitL8-ByteShort/opencode-model-control/releases) lists published versioned tarballs and checksums. Download the exact release asset, verify its SHA-256 against that release's checksum, then install the local file with `npm install --global /absolute/path/to/downloaded-package.tgz`. Historical package digests are recorded in the [historical package ledger](https://github.com/BitL8-ByteShort/opencode-model-control/blob/v0.2.1/packages/README.md). The [release checklist](docs/releasing.md) contains the maintainer-only 0.3.0 publication and verification procedure.
The [GitHub release index](https://github.com/BitL8-ByteShort/opencode-model-control/releases) lists published versioned tarballs and checksums. Download the exact release asset, verify its SHA-256 against that release's checksum, then install the local file with `npm install --global /absolute/path/to/downloaded-package.tgz`. Historical package digests are recorded in the [historical package ledger](https://github.com/BitL8-ByteShort/opencode-model-control/blob/v0.2.1/packages/README.md). The [release checklist](docs/releasing.md) contains the maintainer-only 0.4.0 publication and verification procedure.

## What “Update available models” means (0.3.0)
## What “Update available models” means (0.4.0)

The button asks the installed OpenCode CLI for its effective model list with plugin-aware discovery and `--refresh`. This reflects OpenCode's resolved provider configuration, including its provider and model filters.

Expand All @@ -129,16 +129,22 @@ Catalog state is deliberately split into four concepts:

## Free-first, Paid-first, and pricing evidence

Pricing is matched by the exact provider/full model key and API identity (model ID, npm adapter, and normalized endpoint). A similarly named model, a `-free` suffix, arbitrary CLI zeros, and bundled historical evidence cannot authorize free routing. Model Control fetches the fixed public `https://models.dev/api.json` endpoint without credentials; URLs inside metadata are never fetched. Complete, finite, nonnegative input/output rates are required. Every supported supplied billing dimension counts: reasoning, cache read/write, audio input/output, context tiers, legacy over-200k rates, and experimental modes. With complete valid evidence, any positive rate means paid; all supplied rates must be valid and exactly zero for free. Missing, malformed, unsupported, or conflicting evidence is unknown and blocked. Complete positive CLI evidence can establish `reported-paid` when independent evidence does not contradict it; CLI zero cannot establish free.
Pricing is matched by the exact provider/full model key and API identity (model ID, npm adapter, and normalized endpoint). A similarly named model, a `-free` suffix, arbitrary CLI zeros, and bundled historical evidence cannot authorize free routing. Model Control fetches the fixed public `https://models.dev/api.json` endpoint without credentials; URLs inside metadata are never fetched. Complete, finite, nonnegative input/output rates are required. Every supported supplied billing dimension counts: reasoning, cache read/write, audio input/output, context tiers, legacy over-200k rates, and experimental modes. With complete valid evidence, any positive rate means paid; all supplied rates must be valid and exactly zero for free. Missing, malformed, unsupported, or conflicting pricing evidence is unknown. It cannot authorize Free or legacy verified-price Paid routing; configured Paid routing instead requires an eligible host connection. Complete positive CLI evidence can establish `reported-paid` when independent evidence does not contradict it; CLI zero cannot establish free.

Pricing evidence expires after **24 hours**, checked at route time even without another refresh. Successful HTTP 200 or cached 304 revalidation renews public-source freshness; a failed attempt does not. Cached evidence remains usable only until its existing expiry. Public-source digests and timestamps describe retrieved metadata, not a billing guarantee or model-quality score.

**Automatically include new models** defaults on. A model with `selection: "policy"` (including an absent control) follows that setting and the saved Free/Paid policy. Free permits current verified-free evidence only; Paid permits known-paid and verified-free models and prefers paid after hard gates. Saving Paid with auto-include on authorizes future eligible known-paid models without a separate click for every new model. Turning auto-include off excludes policy-following models; explicit enables still apply. An explicit disable always wins. An enable or role pin cannot bypass unknown/expired pricing, availability, capabilities, or cost policy.
**Automatically include new models** defaults on. A model with `selection: "policy"` (including an absent control) follows that setting and the saved Free/Paid policy. Free permits current verified-free evidence only. Configured Paid permits eligible host connections without requiring a public estimate. Saving that Paid mode with auto-include on authorizes future eligible configured models without a separate click for every new model. Turning auto-include off excludes policy-following models; explicit enables still apply. An explicit disable always wins. An enable or role pin cannot bypass availability, capabilities, connection binding, or the selected policy. Free and legacy verified-price Paid still require current pricing evidence.

Selecting a compatible role model can explicitly enable it in the draft; selecting Automatic changes the role choice without writing inferred model enables. **Save changes** commits user intent. Refresh never adds inferred controls or rewrites saved intent.

Selecting **Paid** can incur charges under the active OpenCode provider account. Model Control does not enforce provider-side budgets.

The **Configured connections** panel separates billing from public prices. You can declare subscription, metered API, prepaid, local, or free billing for an otherwise unreported connection; this is labeled as your declaration, not verified entitlement. Host or adapter evidence takes precedence. Explicit role assignments bind to the selected connection. Changes exposed by OpenCode require review; credential changes the host does not expose cannot be detected.

For a custom provider that maps models to different endpoints or SDK adapters, any change to that model-to-route mapping requires reviewing its saved pins and billing declaration. Adding a model to such a mixed provider can also invalidate the binding conservatively. New models on an unchanged shared endpoint retain the existing binding. Quota from an old binding is discarded.

Captured usage retains the billing label and binding observed at dispatch. It separates tokens and OpenCode-recorded costs by currency, preserves missing values, and reports partial coverage. Subscription quota is shown only when reported; API-equivalent estimates and actual charges are not invented from unavailable token semantics.

The model detail view preserves OpenCode's effective report separately from supplemental public metadata: input/output modalities, tool calls, reasoning and reasoning options, structured output, temperature, attachments, interleaving, and context/input/output limits. Unknown (`null` or absent) is distinct from an explicit `false`; context/input/output limits are positive integers when known. Supplemental data can explain a model but cannot expand OpenCode's effective modalities or tool access. Exact API mismatches discard supplemental evidence. Capability-derived role profiles refresh without changing curated restrictions, ranking, or benchmark qualification.

## Seamless routing boundaries
Expand All @@ -155,7 +161,7 @@ A vision-worker assignment is eligible only when OpenCode reports that the exact

All four stable managed agents are installed without baked-in `model` fields: `omc-router`, `omc-code-worker`, `omc-vision-worker`, and `omc-reviewer`. The local plugin reads coherent saved settings/catalog state for every owned turn, including text, specialist tasks, and ordinary resumed tasks. It intersects eligible catalog models with the current OpenCode instance's loaded provider inventory. Saving A → B takes effect on the next owned turn when B is already loaded; ordinary policy changes do not rewrite config or require reconnecting.

At `chat.params`, the plugin rechecks current policy, price expiry, loaded inventory, exact provider/model/API identity, endpoint/transport, effective capabilities, and effective rates before inference. Missing or corrupt saved state, disabled or unavailable selections, incompatible effective metadata, and unknown pricing fail closed. An explicit pin is never silently replaced. Unrelated OpenCode agents keep their own selections.
At `chat.params`, the plugin rechecks current policy, price expiry, loaded inventory, exact provider/model/API identity, endpoint/transport, effective capabilities, and effective rates before inference. Missing or corrupt saved state, disabled or unavailable selections, revoked or changed connection bindings, and incompatible effective metadata fail closed. Unknown pricing blocks Free and legacy verified-price Paid; configured Paid access can proceed without an estimate. An explicit pin is never silently replaced. Unrelated OpenCode agents keep their own selections.

A newly discovered C absent from the running host inventory needs an explicit OpenCode reload/restart. It blocks with `OMC_HOST_MODEL_MISSING`; automatic roles may choose eligible already-loaded models. OpenCode 1.18.22/1.18.28 sanitize HTTP plugin failures to `UnknownError`, so the actionable reload guidance is a same-directory TUI toast/event. Headless consumers must read the instance event stream to receive that text. Model Control never disposes or restarts an OpenCode instance automatically. Changes to installed agent instructions/permissions, package or plugin paths, or the optional default agent require **Update connection** and an OpenCode restart.

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The manual runtime access check is never automatic. It requires explicit provide

Catalog refresh sends a credential-free request only to the fixed `https://models.dev/api.json` metadata endpoint, with JSON accept and conditional ETag/Last-Modified headers. Redirects are rejected; request time and response size are bounded (8 seconds, 32 MiB). No metadata-provided URL is fetched and no prompt, attachment, usage, selected-model list, local config, or provider credential is sent. Public metadata servers still receive ordinary request/network metadata. The private conditional cache carries an exact source digest and retrieval/expiry times; failed retrieval never extends the 24-hour pricing lifetime. Refresh checks run every 15 minutes while active and on stale startup or manual request.

Pricing is matched by the exact provider/full model key and API identity (model ID, npm adapter, and normalized endpoint). A similarly named model, a `-free` suffix, arbitrary CLI zeros, and bundled historical evidence cannot authorize free routing. Model Control fetches the fixed public `https://models.dev/api.json` endpoint without credentials; URLs inside metadata are never fetched. Complete, finite, nonnegative input/output rates are required. Every supported supplied billing dimension counts: reasoning, cache read/write, audio input/output, context tiers, legacy over-200k rates, and experimental modes. With complete valid evidence, any positive rate means paid; all supplied rates must be valid and exactly zero for free. Missing, malformed, unsupported, or conflicting evidence is unknown and blocked. Complete positive CLI evidence can establish `reported-paid` when independent evidence does not contradict it; CLI zero cannot establish free.
Pricing is matched by the exact provider/full model key and API identity (model ID, npm adapter, and normalized endpoint). Raw empty, absent, and null URLs are unspecified SDK defaults, not a wildcard for custom endpoints. A similarly named model, a `-free` suffix, arbitrary CLI zeros, and bundled historical evidence cannot authorize free routing. Model Control fetches the fixed public `https://models.dev/api.json` endpoint without credentials; URLs inside metadata are never fetched. Complete, finite, nonnegative input/output rates are required. Every supported supplied billing dimension counts: reasoning, cache read/write, audio input/output, context tiers, legacy over-200k rates, and experimental modes. With complete valid evidence, any positive rate means paid; all supplied rates must be valid and exactly zero for free. Missing, malformed, unsupported, or conflicting evidence is unknown. Unknown prices cannot authorize Free or migrated verified-pricing Paid. After the user saves configured-connection Paid access, a configured host route may be used without a public estimate. Complete positive CLI evidence can establish `reported-paid` when independent evidence does not contradict it; CLI zero cannot establish free, and CLI cost cannot override a public-price route mismatch. Provider-owned authentication transports are not inspected or logged.

Pricing evidence expires after **24 hours**, checked at route time even without another refresh. Successful HTTP 200 or cached 304 revalidation renews public-source freshness; a failed attempt does not. Cached evidence remains usable only until its existing expiry. Public-source digests and timestamps describe retrieved metadata, not a billing guarantee or model-quality score.

Expand Down
Loading