Skip to content
79 changes: 50 additions & 29 deletions ARCHITECTURE-MAP.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ All notable changes to the **OpenCode Go BYOK Provider** extension are documente
- `src/provider/` — `OpenCodeProvider` class, `definitions` (PROVIDERS table + model types), `messages`/`tokens` (message conversion + token estimation), `settings` (schema/getSettings/limits/capabilities), `visionProxy`.
- `src/models/` — `metadata`, `modelLimits`, `modelCapabilities`, `modelNames`, `pricing`, `metadataFetcher` (models.dev cache).
- `src/commands/` — provider, agent-window, diagnostics and thinking-picker command handlers; `src/request/headers.ts` for the OpenCode request headers.
- **`[Internal]` Remaining oversized files split to enforce the ≤600-line limit (no behavior change).** `src/provider/OpenCodeProvider.ts` (1259 lines) now delegates to `chatPrep.ts` (request preparation: message conversion, vision proxy, history trimming, budgets), `modelInfo.ts` (`provideLanguageModelChatInformation` assembly), `modelList.ts` (`ModelListFetcher` with retry/cache), `transportLog.ts` (`TransportSummaryLog`), `providerDialogs.ts` (Manage/Test Connection flows) and `providerUtils.ts`; the class itself is 584 lines. `src/usage/tracker.ts` is split into `trackerTypes.ts`, `trackerWindows.ts` and `trackerSummary.ts` (pure summary builders taking an injected context). The goUsageTracker tests are split into focused suites sharing `src/test/helpers/goUsageTestUtils.ts`.
- `extension.ts` is now a thin entry (~400 lines) that only wires activation + command registration. The two compat barrels (`streaming.ts`, `goUsageTracker.ts`) are removed and every importer references canonical paths. All behavior-preserving — verified by `npm run compile` + 291 unit tests + mock-server retry E2E (`npm run test-retry`) + `npm run lint`.

- **`[Internal]` Data-driven model registry (`src/core/registry.ts`).** The transport router and the thinking-family detector previously each owned a hardcoded model-prefix table. Both now read ONE data-driven table: `MODEL_REGISTRY` rows map model-family patterns → `{ endpointKind, sdkPackage, thinkingFamily, vendors? }`. `resolveModelRouting()` honors per-vendor restrictions (e.g. MiniMax `m2.x` → Messages on Go, Gemini → Google on Zen); `thinkingFamily()` reads the same table vendor-agnostically. Adding a new model family = adding one row (+ optionally a thinking strategy class). Context limits / capabilities stay metadata-driven (live models.dev) rather than duplicated in a static table. Behavior-preserving — verified by 14 new registry tests (305 total).
Expand Down
1 change: 1 addition & 0 deletions docs/devlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,7 @@ rg -n "sk-[A-Za-z0-9]|apiKey.*[A-Za-z0-9]{20,}|Authorization: Bearer [A-Za-z0-9]
| Date | Version | Summary |
| ---------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 2026-08-21 | docs | Docs sync for PR wave #157–#172: created issues 72 (PR #164 think-tag force-strip) + 73 (#162 GLM 5.3) + 74 (PR #157 bug-hunt) + features 18 (Muse Spark 1.2); features/02 + issues/36 updated; 3 missing CHANGELOG [Unreleased] entries added (PR #157, #160, #164); devlog refreshed. Open PRs #161, #170 tracked. |
| 2026-08-21 | refactor/file-size-limit | Completed the ≤600-line file-size policy — split the remaining oversized modules with zero behavior change: `src/provider/OpenCodeProvider.ts` (1259→584) into `chatPrep`, `modelInfo`, `modelList`, `transportLog`, `providerDialogs`, `providerUtils`; `src/usage/tracker.ts` (989) into `trackerTypes`/`trackerWindows`/`trackerSummary`; `src/test/goUsageTracker.test.ts` (904) into focused suites + `test/helpers/goUsageTestUtils`. Full lint gate green after each split. 3 commits on `refactor/split-oversized-files`. |
| 2026-08-13 | refactor/thinking-request-modules | Thinking refactor (per-provider strategy classes + single VS Code per-model config authority + removed globalState shadow + `effectiveModelId`) + request module split (`src/request/`) + Windows lint fixes (`.cmd` shims + `.gitattributes` LF). 6 commits. CHANGELOG [Unreleased] updated. |
| 2026-06-13 | docs | Deep audit — all 4 🟢 Active docs verified against codebase + git history + CHANGELOG. All marked ✅ Solved: issue #19 (PR #15 merged), references #01 (research complete), architecture #01 (living ref complete), issue #01 (all code fixed v0.1.9/v0.1.10, remaining tool-call loop is model behavior not code bug). 0 Active docs remain. |
| 2026-06-13 | docs | Rewrote devlog into work-context format and flagged unrelated `WORK-CONTEXT.md` content |
Expand Down
134 changes: 4 additions & 130 deletions src/models/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GO_VENDOR, ZEN_VENDOR, type ProviderVendor, type AllProviderVendor } from "../providerTypes";
import { MODEL_LIMITS_BY_PROVIDER, MODELS_WITHOUT_TEMPERATURE, VISION_CAPABLE_MODELS } from "./modelTables";

export interface BaseModelLimits {
contextWindow: number;
Expand Down Expand Up @@ -160,140 +161,13 @@ const MODELS_DEV_PROVIDER_BY_VENDOR: Record<ProviderVendor, keyof ModelsDevRespo
[ZEN_VENDOR]: "opencode",
};

const MODEL_LIMITS_BY_PROVIDER: Record<ProviderVendor, Record<string, BaseModelLimits | undefined>> = {
[GO_VENDOR]: {
// OpenCode Go caps completion at 131072 even though models.dev still lists
// output: 384000 — the gateway rejects larger values with HTTP 400 (#171).
"deepseek-v4-flash": { contextWindow: 1000000, maxOutputTokens: 131072 },
"deepseek-v4-pro": { contextWindow: 1000000, maxOutputTokens: 384000 },
"mimo-v2.5": { contextWindow: 1000000, maxOutputTokens: 128000 },
"mimo-v2.5-pro": { contextWindow: 1048576, maxOutputTokens: 128000 },
"mimo-v2-omni": { contextWindow: 262144, maxOutputTokens: 128000 },
"mimo-v2-pro": { contextWindow: 1048576, maxOutputTokens: 128000 },
"kimi-k2.7-code": { contextWindow: 256000, maxOutputTokens: 262144 },
"kimi-k2.6": { contextWindow: 262144, maxOutputTokens: 65536 },
"kimi-k2.5": { contextWindow: 262144, maxOutputTokens: 65536 },
"glm-5.1": { contextWindow: 202752, maxOutputTokens: 32768 },
"glm-5": { contextWindow: 202752, maxOutputTokens: 32768 },
"minimax-m3": { contextWindow: 512000, maxOutputTokens: 131072 },
"minimax-m2.7": { contextWindow: 204800, maxOutputTokens: 131072 },
"minimax-m2.5": { contextWindow: 204800, maxOutputTokens: 65536 },
"minimax-m2.1": { contextWindow: 204800, maxOutputTokens: 131072 },
"minimax-m2": { contextWindow: 204800, maxOutputTokens: 131072 },
"qwen3.7-max": { contextWindow: 1000000, maxOutputTokens: 65536 },
"qwen3.7-plus": { contextWindow: 262144, maxOutputTokens: 65536 },
"qwen3.6-plus": { contextWindow: 262144, maxOutputTokens: 65536 },
"qwen3.5-plus": { contextWindow: 262144, maxOutputTokens: 65536 },
"gpt-5.6-luna": { contextWindow: 1050000, maxOutputTokens: 128000 },
"hy3-preview": { contextWindow: 256000, maxOutputTokens: 64000 },
"muse-spark-1.2-contributor": { contextWindow: 1048576, maxOutputTokens: 131072 },
},
[ZEN_VENDOR]: {
"claude-opus-4-7": { contextWindow: 1000000, maxOutputTokens: 128000 },
"claude-opus-4-6": { contextWindow: 1000000, maxOutputTokens: 128000 },
"claude-opus-4-5": { contextWindow: 200000, maxOutputTokens: 64000 },
"claude-opus-4-1": { contextWindow: 200000, maxOutputTokens: 32000 },
"claude-sonnet-4-6": { contextWindow: 1000000, maxOutputTokens: 64000 },
"claude-sonnet-4-5": { contextWindow: 1000000, maxOutputTokens: 64000 },
"claude-sonnet-4": { contextWindow: 1000000, maxOutputTokens: 64000 },
"claude-haiku-4-5": { contextWindow: 200000, maxOutputTokens: 64000 },
"deepseek-v4-flash-free": { contextWindow: 200000, maxOutputTokens: 128000 },
"gemini-3.5-flash": { contextWindow: 1048576, maxOutputTokens: 65536 },
"gemini-3.1-pro": { contextWindow: 1048576, maxOutputTokens: 65536 },
"gemini-3-flash": { contextWindow: 1048576, maxOutputTokens: 65536 },
"glm-5.1": { contextWindow: 204800, maxOutputTokens: 131072 },
"glm-5": { contextWindow: 204800, maxOutputTokens: 131072 },
"gpt-5.5": { contextWindow: 1050000, maxOutputTokens: 128000 },
"gpt-5.5-pro": { contextWindow: 1050000, maxOutputTokens: 128000 },
"gpt-5.4": { contextWindow: 1050000, maxOutputTokens: 128000 },
"gpt-5.4-pro": { contextWindow: 1050000, maxOutputTokens: 128000 },
"gpt-5.4-mini": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.4-nano": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.3-codex": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.3-codex-spark": { contextWindow: 128000, maxOutputTokens: 128000 },
"gpt-5.2": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.2-codex": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.1": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.1-codex": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.1-codex-max": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5.1-codex-mini": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5-codex": { contextWindow: 400000, maxOutputTokens: 128000 },
"gpt-5-nano": { contextWindow: 400000, maxOutputTokens: 128000 },
"grok-build-0.1": { contextWindow: 256000, maxOutputTokens: 256000 },
"kimi-k2.6": { contextWindow: 262144, maxOutputTokens: 65536 },
"kimi-k2.5": { contextWindow: 262144, maxOutputTokens: 65536 },
"minimax-m2.7": { contextWindow: 204800, maxOutputTokens: 131072 },
"minimax-m2.5": { contextWindow: 204800, maxOutputTokens: 131072 },
"minimax-m2.5-free": { contextWindow: 204800, maxOutputTokens: 131072 },
"qwen3.6-plus": { contextWindow: 262144, maxOutputTokens: 65536 },
"qwen3.6-plus-free": { contextWindow: 262144, maxOutputTokens: 65536 },
"qwen3.5-plus": { contextWindow: 262144, maxOutputTokens: 65536 },
"trinity-large-preview-free": { contextWindow: 131072, maxOutputTokens: 131072 },
"nemotron-3-super-free": { contextWindow: 204800, maxOutputTokens: 128000 },
"big-pickle": { contextWindow: 200000, maxOutputTokens: 128000 },
"muse-spark-1.2-contributor-free": { contextWindow: 1048576, maxOutputTokens: 131072 },
},
};

/**
* Models where the `temperature` request parameter is unsupported / deprecated.
* The upstream API rejects any non-default temperature with HTTP 400 for these.
* Mirrors the models.dev `temperature: false` flag so the extension still
* omits temperature even when the live metadata fetch fails.
*/
const MODELS_WITHOUT_TEMPERATURE = new Set([
// Kimi K2.7-code: Moonshot API returns "invalid temperature: only 1 is allowed"
"kimi-k2.7-code",
]);

export function isFreeModel(modelId: string): boolean {
return FREE_ZEN_MODEL_IDS.has(modelId) || modelId.endsWith("-free");
}

export const VISION_CAPABLE_MODELS = new Set([
"minimax-m2.7",
"minimax-m2.5",
"minimax-m2.5-free",
"kimi-k2.7-code",
"kimi-k2.6",
"kimi-k2.5",
"glm-5.1",
"glm-5",
"mimo-v2.5",
"mimo-v2.5-pro",
"mimo-v2-omni",
"mimo-v2-pro",
"claude-opus-4-7",
"claude-opus-4-6",
"claude-opus-4-5",
"claude-opus-4-1",
"claude-sonnet-4-6",
"claude-sonnet-4-5",
"claude-sonnet-4",
"claude-haiku-4-5",
"gemini-3.5-flash",
"gemini-3.1-pro",
"gemini-3-flash",
"gpt-5.5",
"gpt-5.5-pro",
"gpt-5.4",
"gpt-5.4-pro",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-5.3-codex",
"gpt-5.2",
"gpt-5.2-codex",
"gpt-5.1",
"gpt-5.1-codex",
"gpt-5.1-codex-max",
"gpt-5.1-codex-mini",
"gpt-5",
"gpt-5-codex",
"gpt-5-nano",
"grok-build-0.1",
"gpt-5.6-luna",
]);
// Static per-model data tables live in `modelTables.ts`; re-exported here so
// existing import paths keep working.
export { VISION_CAPABLE_MODELS };

export function isFreshModelMetadata(snapshot: CachedModelMetadataSnapshot): boolean {
return Date.now() - snapshot.fetchedAt < MODEL_METADATA_CACHE_TTL_MS;
Expand Down
Loading
Loading