Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
7fb09e4
fix(ci): drain ESLint/dead-code/vitest base-reds on release/v3.8.50 (…
hartmark Aug 19, 2026
8d3129f
Merge remote-tracking branch 'origin/release/v3.8.50' into fix/releas…
hartmark Aug 21, 2026
d098114
fix(combo): guarantee combo loops terminate with an actionable error …
herjarsa Aug 21, 2026
74c9e7e
feat(combo): Auto-Combo snapshot generation/duplication in the UX (#1…
swingtempo Aug 21, 2026
d6737bf
fix(zed-hosted): add connection test support for Zed Hosted Models (#…
Hsia97 Aug 21, 2026
374b387
fix(opencode): route Muse Spark 1.2 models to OpenAI Responses API (#…
zoser69 Aug 21, 2026
1d4c4cd
fix(sse): re-run strict system hoist after format translation (#10803)
Kizuno18 Aug 21, 2026
b87056a
feat(a2a): A2A v1.0 client compatibility (SendMessage alias + v1.0 ag…
wpec Aug 21, 2026
b661b71
fix(sse,compression): graceful silent-close truncation + compressTool…
minhlongs Aug 21, 2026
6098954
test(providers): regenerate translate-path golden snapshot for freebu…
hartmark Aug 21, 2026
fa0cd5a
fix(cursor): discover account Agent endpoint (#10804)
tuandinh0801 Aug 21, 2026
16fc433
fix(skills): make marketplace installs available to API keys (#10854)
kriptoburak Aug 21, 2026
6fd4040
feat(cli): support Grok Build custom host configuration (#10830)
tuandinh0801 Aug 21, 2026
5bf694f
chore(deps): bump github/codeql-action from 4.37.6 to 4.37.7 (#10928)
dependabot[bot] Aug 21, 2026
af47a5f
chore(deps): bump github/codeql-action/analyze from 4.37.6 to 4.37.7 …
dependabot[bot] Aug 21, 2026
34f65e1
chore(deps): bump github/codeql-action/init from 4.37.6 to 4.37.7 (#1…
dependabot[bot] Aug 21, 2026
1d29188
deps: bump the development group with 2 updates (#10932)
dependabot[bot] Aug 21, 2026
7fa6528
deps: bump the production group with 5 updates (#10931)
dependabot[bot] Aug 21, 2026
24f31cf
feat(command-code): expose reasoning effort suffixes (#10884)
excessivechaos Aug 21, 2026
b580992
feat(opencode-go): expose Muse Spark reasoning effort aliases (#10883)
excessivechaos Aug 21, 2026
fac2a93
fix(dashboard): guard non-string apiKey in CLI tool cards (#10872)
Rahulsharma0810 Aug 21, 2026
967b56a
fix(cline): stop generating proxy task ids (#10279)
arafatkatze Aug 21, 2026
45b42ec
fix(compression): bump vendored GCF with numeric-domain and surplus f…
blackwell-systems Aug 21, 2026
3805494
feat(vision-bridge): consolidate vision bridge fixes (catalog verdict…
Stazyu Aug 21, 2026
825e2ab
fix(opencode-plugin): respect log level in provider.models() catalog …
tientien17 Aug 21, 2026
fdeac49
feat(providers): restore per-connection upstream timeout tier (#10885)
maxmad64bis Aug 21, 2026
1b16e12
fix(quality): rebaseline file-size for #10859 own growth (missed at m…
hartmark Aug 21, 2026
788be6d
fix(combo): clear stale sticky pins when stickiness is disabled (#10907)
excessivechaos Aug 21, 2026
467a804
fix(model): return model_not_found for unrecognized prefix models whe…
rqzbeh Aug 21, 2026
354d3a2
fix(models): persist apiFormat, targetFormat, and supportsVision over…
rqzbeh Aug 21, 2026
8b9dc7d
fix(auth): add opencode/opencode-zen to search pairs for credential r…
rqzbeh Aug 21, 2026
0940bb6
fix(search): enforce blockedProviders setting on search endpoint (#10…
rqzbeh Aug 21, 2026
9fd19f5
fix(settings): add customSystemPrompt fields to updateSettingsSchema …
rqzbeh Aug 21, 2026
6eaa737
fix(relay): resolve x-relay-path through the shared guard in the CF w…
ntdat812 Aug 21, 2026
4fa204d
fix(relay): share one private-host guard across the three relay worke…
ntdat812 Aug 21, 2026
e4a2417
feat(auth): add OIDC password-login disabler when OIDC is active (#10…
MeRezaRezaei Aug 21, 2026
31193af
fix(test): bump EXPECTED_FEATURE_FLAG_COUNT to 52 for #10889 own grow…
hartmark Aug 21, 2026
9b95282
fix(providers): stop the loopback readiness gate from memorizing fail…
maxmad64bis Aug 21, 2026
4c0b54a
feat(rankings): expose provider reliability alongside free provider r…
maxmad64bis Aug 21, 2026
10deb5c
fix(build): scope standalone type:module to worker dirs so server.js …
arminanton Aug 21, 2026
c0fd109
fix(dashboard): show account email/name in Utilization Account Split …
ZaimMarzuki Aug 21, 2026
6663d70
provider(agnes): refresh chat catalog to 2026.07.30 (openai chat/comp…
oyi77 Aug 21, 2026
65dcb1d
feat(resilience): stop retrying sibling connections after an IP-bucke…
maxmad64bis Aug 21, 2026
6efb01a
feat(sse): add kimi web token lifecycle manager, rolling auto-refresh…
MeRezaRezaei Aug 21, 2026
53608c8
[v3.8.50] refactor(tls): consolidate 6 TLS client providers into shar…
oyi77 Aug 21, 2026
410a061
fix(security): clear new CodeQL code-scanning alerts (round 2) (#10888)
diegosouzapw Aug 21, 2026
0f35feb
fix(executors): report WS readyState in Meta AI timeout error (#10727…
diegosouzapw Aug 21, 2026
a8ca957
fix: add cold-restart native-driver regression check to electron smok…
diegosouzapw Aug 21, 2026
7011c5f
fix(cli): repair hollow externalized package dirs in nested distDir n…
diegosouzapw Aug 21, 2026
14d2c90
fix(executor): respect apiType="chat" in forceResponsesUpstream (#548…
YunyunZhai Aug 21, 2026
e708030
fix(resilience): make least-used rotate by recording the use it sorts…
ntdat812 Aug 21, 2026
6ec6940
fix(catalog): preserve provider effort tiers (#10953)
xz-dev Aug 21, 2026
28924fe
fix(models): enforce a synced model's real context window and default…
Neuron-Mr-White Aug 21, 2026
1c1e45c
fix(desktop): pin the NSIS artifact name so the Windows updater stops…
ntdat812 Aug 21, 2026
1f09e2f
feat(rankings): report what a free provider actually served (#10926)
maxmad64bis Aug 21, 2026
d1e5a57
fix(onboarding): add warning when skipping password in setup wizard (…
krishna3554 Aug 21, 2026
63c0125
feat(providers): tool calling for copilot-m365-web via router plannin…
acc0mplish Aug 21, 2026
154c294
feat(docker): add hardened Linux VPS deployment (#10623)
freudantunes Aug 21, 2026
2603816
merge release/v3.8.50 into fix-10778
hartmark Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
languages: javascript-typescript
queries: security-extended
- uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
category: "/language:javascript-typescript"
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ jobs:
- name: Upload Trivy SARIF to Security tab
if: needs.prepare.outputs.version != 'main'
continue-on-error: true
uses: github/codeql-action/upload-sarif@v4.37.6
uses: github/codeql-action/upload-sarif@v4.37.7
with:
sarif_file: trivy-results.sarif
category: trivy-image
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/electron-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,14 @@ jobs:

- name: Smoke packaged Electron app (Linux)
if: matrix.platform == 'linux'
# #7592: also cold-restart against the same DATA_DIR and assert a
# native SQLite driver (not the sql.js WASM fallback) is selected on
# the second launch β€” blocking here since Linux has no Windows-style
# sandbox caveats that would make it flaky.
env:
ELECTRON_SMOKE_TIMEOUT_MS: 60000
ELECTRON_SMOKE_STREAM_LOGS: "1"
ELECTRON_SMOKE_COLD_RESTART: "1"
run: xvfb-run -a npm run electron:smoke:packaged

- name: Collect installers
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,4 @@ docker-compose.yml.bak

# Ad-hoc test sandboxes (never tracked β€” may contain local DBs)
/.sandbox/
.aider*
125 changes: 74 additions & 51 deletions @omniroute/opencode-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ import {
type FreeModelFreeType,
} from "./naming.js";

/**
* Minimal leveled logger sink accepted by the default fetchers and the static
* catalog builder. A full `Logger` satisfies it structurally; the config hook
* injects the same partial shape (see `createOmniRouteConfigHook` deps).
*/
type OmniRouteLoggerSink = {
error?: (message: string, ...args: unknown[]) => void;
warn: (message: string, ...args: unknown[]) => void;
debug?: (message: string, ...args: unknown[]) => void;
};

/**
* Zod schema for plugin options accepted as the second element of the
* `plugin: [name, opts]` tuple in opencode.json. Strict by design β€” unknown
Expand Down Expand Up @@ -791,13 +802,18 @@ export async function forceSyncOmniRouteModels(args: {
try {
rawCombos = await combosFetcher(auth.baseURL, auth.managementReadToken, 10_000);
} catch (err) {
console.warn("[omniroute-plugin] force sync: combos fetch failed", err);
logger.warn("force sync: combos fetch failed", err);
}
}
let rawAutoCombos: OmniRouteRawAutoCombo[] = [];
if (wantAutoCombos) {
try {
rawAutoCombos = await autoCombosFetcher(auth.baseURL, auth.managementReadToken, 5_000);
rawAutoCombos = await autoCombosFetcher(
auth.baseURL,
auth.managementReadToken,
5_000,
logger
);
} catch {
/* soft-fail */
}
Expand Down Expand Up @@ -1089,7 +1105,7 @@ export const OmniRoutePlugin: Plugin = async (_input, options) => {

return {
auth: createOmniRouteAuthHook(resolved),
provider: createOmniRouteProviderHook(resolved, { cache: sharedCache }),
provider: createOmniRouteProviderHook(resolved, { cache: sharedCache, logger }),
config: configWithSyncCommand,
tool: {
omniroute_sync_models: syncTool,
Expand Down Expand Up @@ -1676,7 +1692,8 @@ export interface OmniRouteRawAutoCombo {
export type OmniRouteAutoCombosFetcher = (
baseURL: string,
apiKey: string,
timeoutMs?: number
timeoutMs?: number,
logger?: OmniRouteLoggerSink
) => Promise<OmniRouteRawAutoCombo[]>;

/**
Expand All @@ -1688,9 +1705,11 @@ export type OmniRouteAutoCombosFetcher = (
export const defaultOmniRouteAutoCombosFetcher: OmniRouteAutoCombosFetcher = async (
baseURL,
apiKey,
timeoutMs = 5_000
timeoutMs = 5_000,
logger?: OmniRouteLoggerSink
) => {
if (!apiKey || !baseURL) return [];
const log = logger ?? _logger;

const trimmed = trimTrailingSlashes(baseURL);
const root = trimmed.replace(/\/v\d+$/, "");
Expand All @@ -1709,15 +1728,11 @@ export const defaultOmniRouteAutoCombosFetcher: OmniRouteAutoCombosFetcher = asy
});
// 404 = endpoint not deployed yet β€” expected during rollout
if (res.status === 404) {
console.warn(
`[omniroute-plugin] /api/combos/auto not available (404) β€” auto combos disabled`
);
log.warn(`/api/combos/auto not available (404) β€” auto combos disabled`);
return [];
}
if (!res.ok) {
console.warn(
`[omniroute-plugin] /api/combos/auto failed: ${res.status} ${res.statusText} β€” auto combos disabled`
);
log.warn(`/api/combos/auto failed: ${res.status} ${res.statusText} β€” auto combos disabled`);
return [];
}
const body = (await res.json()) as unknown;
Expand All @@ -1735,8 +1750,8 @@ export const defaultOmniRouteAutoCombosFetcher: OmniRouteAutoCombosFetcher = asy
return out;
} catch (err) {
// Network error, timeout, abort β€” all non-fatal
console.warn(
`[omniroute-plugin] /api/combos/auto fetch failed: ${err instanceof Error ? err.message : String(err)} β€” auto combos disabled`
log.warn(
`/api/combos/auto fetch failed: ${err instanceof Error ? err.message : String(err)} β€” auto combos disabled`
);
return [];
} finally {
Expand Down Expand Up @@ -2935,10 +2950,7 @@ export function passesModelAllowlist(
* filter is set, all combos pass. Combos with zero resolvable members pass
* (mirrors `isUsableCombo` semantics).
*/
export function passesComboAllowlist(
combo: OmniRouteRawCombo,
visible?: ModelListFilter
): boolean {
export function passesComboAllowlist(combo: OmniRouteRawCombo, visible?: ModelListFilter): boolean {
if (!visible) return true;
const steps = Array.isArray(combo.models) ? combo.models : [];
if (steps.length === 0) return true;
Expand Down Expand Up @@ -3130,9 +3142,15 @@ export function createOmniRouteProviderHook(
providersFetcher?: OmniRouteProvidersFetcher;
now?: () => number;
cache?: OmniRouteFetchCache;
logger?: _Logger;
} = {}
): ProviderHook {
const resolved = resolveOmniRoutePluginOptions(opts);
const logger =
deps.logger ??
createLogger(
resolved.features?.startupDebug ? "debug" : (resolved.features?.logLevel ?? "warn")
);
const fetcher = deps.fetcher ?? defaultOmniRouteModelsFetcher;
// T-05: combo discovery merges `/api/combos` entries into the same map as
// `/v1/models`. Default fetcher is declared further down the file; the
Expand Down Expand Up @@ -3206,8 +3224,8 @@ export function createOmniRouteProviderHook(
: undefined) ??
"";
if (!baseURL) {
console.warn(
`[omniroute-plugin] provider.models(${resolved.providerId}): ` +
logger.error(
`provider.models(${resolved.providerId}): ` +
`no baseURL resolvable β€” checked plugin opts, auth.json, and provider config. ` +
`Set baseURL in opencode.json plugin options or run \`opencode connect ${resolved.providerId}\` with a baseURL.`
);
Expand Down Expand Up @@ -3238,8 +3256,8 @@ export function createOmniRouteProviderHook(
rawModels = await fetcher(baseURL, apiKey, 10_000);

// T-05: combos fetch is best-effort, gated by features.combos.
// Soft-fail on any error: emit a console.warn and fall back to a
// models-only catalog. Rationale: /api/combos requires a
// Soft-fail on any error: emit a warn-level diagnostic and fall back
// to a models-only catalog. Rationale: /api/combos requires a
// management-scoped key and OmniRoute may not have any combos
// provisioned. Hard-failing when combos are optional would
// silently hide the whole provider from OC's picker.
Expand All @@ -3248,10 +3266,7 @@ export function createOmniRouteProviderHook(
try {
rawCombos = await combosFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
console.warn(
"[omniroute-plugin] combos fetch failed, falling back to models-only catalog",
err
);
logger.warn("combos fetch failed, falling back to models-only catalog", err);
}
}

Expand All @@ -3261,7 +3276,7 @@ export function createOmniRouteProviderHook(
rawAutoCombos = [];
if (wantAutoCombos) {
try {
rawAutoCombos = await autoCombosFetcher(baseURL, managementReadToken, 5_000);
rawAutoCombos = await autoCombosFetcher(baseURL, managementReadToken, 5_000, logger);
} catch {
// Already handled inside the default fetcher β€” this catch
// is belt-and-suspenders for injected stubs.
Expand All @@ -3275,10 +3290,7 @@ export function createOmniRouteProviderHook(
try {
rawEnrichment = await enrichmentFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
console.warn(
"[omniroute-plugin] enrichment fetch failed, falling back to raw ids",
err
);
logger.warn("enrichment fetch failed, falling back to raw ids", err);
}
}

Expand All @@ -3293,7 +3305,7 @@ export function createOmniRouteProviderHook(
10_000
);
} catch (err) {
console.warn("[omniroute-plugin] compression-metadata fetch failed", err);
logger.warn("compression-metadata fetch failed", err);
}
}

Expand All @@ -3307,8 +3319,8 @@ export function createOmniRouteProviderHook(
try {
rawConnections = await providersFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
console.warn(
"[omniroute-plugin] /api/providers fetch failed; usableOnly filter disabled for this refresh",
logger.warn(
"/api/providers fetch failed; usableOnly filter disabled for this refresh",
err
);
}
Expand All @@ -3327,8 +3339,9 @@ export function createOmniRouteProviderHook(
// Debug breadcrumb: surface fetch result so operators can confirm
// the dynamic pipeline fired and how much catalog OmniRoute returned.
// Emitted once per cache miss (TTL refresh) β€” quiet on cache hits.
console.warn(
`[omniroute-plugin] catalog refreshed for providerId=${resolved.providerId} baseURL=${baseURL}: ` +
// Info-level: hidden at the default `warn` level (see #8982).
logger.info(
`catalog refreshed for providerId=${resolved.providerId} baseURL=${baseURL}: ` +
`${rawModels.length} models + ${rawCombos.length} combos + ` +
`${rawEnrichment.size} enrichment entries + ` +
`${rawCompressionCombos.length} compression combos + ` +
Expand Down Expand Up @@ -3608,9 +3621,7 @@ export function createOmniRouteProviderHook(
const dedupeKey = `${cacheKey}::${comboKey}`;
if (!collisionWarned.has(dedupeKey)) {
collisionWarned.add(dedupeKey);
console.warn(
`[omniroute-plugin] combo key "${comboKey}" collides with a model id; combo wins.`
);
logger.warn(`combo key "${comboKey}" collides with a model id; combo wins.`);
}
}
}
Expand All @@ -3628,8 +3639,8 @@ export function createOmniRouteProviderHook(
}

if (pending.length > 0) {
console.warn(
`[omniroute-plugin] ${pending.length} combo(s) could not resolve all nested combo-refs after ${MAX_COMBO_PASSES} passes; they will advertise context=0 to avoid over-claiming.`
logger.warn(
`${pending.length} combo(s) could not resolve all nested combo-refs after ${MAX_COMBO_PASSES} passes; they will advertise context=0 to avoid over-claiming.`
);
}

Expand Down Expand Up @@ -4273,8 +4284,10 @@ export function buildStaticProviderEntry(
enrichment?: OmniRouteEnrichmentMap,
compressionCombos?: OmniRouteCompressionCombo[],
connections?: OmniRouteProviderConnection[],
rawAutoCombos?: OmniRouteRawAutoCombo[]
rawAutoCombos?: OmniRouteRawAutoCombo[],
logger?: OmniRouteLoggerSink
): OmniRouteStaticProviderEntry {
const log = logger ?? _logger;
const models: Record<string, OmniRouteStaticModelEntry> = {};
const rawModelKeys = new Set<string>();

Expand Down Expand Up @@ -4652,8 +4665,8 @@ export function buildStaticProviderEntry(
}

if (pendingStatic.length > 0) {
console.warn(
`[omniroute-plugin] ${pendingStatic.length} combo(s) in the static catalog could not resolve all nested combo-refs after ${MAX_STATIC_COMBO_PASSES} passes; they will be omitted.`
log.warn(
`${pendingStatic.length} combo(s) in the static catalog could not resolve all nested combo-refs after ${MAX_STATIC_COMBO_PASSES} passes; they will be omitted.`
);
}

Expand All @@ -4674,9 +4687,7 @@ export function buildStaticProviderEntry(
const isExpectedRawTwin = autoCombo.id === key && rawModelKeys.has(key);
if (!isExpectedRawTwin && !reportedCollisions.has(key)) {
reportedCollisions.add(key);
console.warn(
`[omniroute-plugin] auto combo key "${key}" collides with an existing model; auto combo wins.`
);
log.warn(`auto combo key "${key}" collides with an existing model; auto combo wins.`);
}
}
models[key] = entry;
Expand Down Expand Up @@ -5347,7 +5358,8 @@ export function createOmniRouteConfigHook(
warmSnapshot = snapshotResult;
// Log snapshot age (accept any age β€” instant beats empty).
const age = (snapshotResult as { writtenAt?: number }).writtenAt;
const ageLabel = typeof age === "number" ? `${Math.round((Date.now() - age) / 3_600_000)}h` : "unknown";
const ageLabel =
typeof age === "number" ? `${Math.round((Date.now() - age) / 3_600_000)}h` : "unknown";
logAt(
"warn",
`config shim: warm startup from disk snapshot (${snapshotResult.rawModels.length} models, age ${ageLabel})`
Expand Down Expand Up @@ -5399,7 +5411,12 @@ export function createOmniRouteConfigHook(
const doAutoCombos = async (): Promise<void> => {
if (!wantAutoCombos) return;
try {
localRawAutoCombos = await autoCombosFetcher(baseURL, managementReadToken, 5_000);
localRawAutoCombos = await autoCombosFetcher(
baseURL,
managementReadToken,
5_000,
logger
);
} catch {
// Already handled inside the default fetcher
}
Expand All @@ -5420,7 +5437,11 @@ export function createOmniRouteConfigHook(
const doCompression = async (): Promise<void> => {
if (!wantCompressionMeta) return;
try {
localRawCompressionCombos = await compressionMetaFetcher(baseURL, managementReadToken, 10_000);
localRawCompressionCombos = await compressionMetaFetcher(
baseURL,
managementReadToken,
10_000
);
} catch (err) {
logAt(
"error",
Expand Down Expand Up @@ -5533,7 +5554,8 @@ export function createOmniRouteConfigHook(
localRawEnrichment,
localRawCompressionCombos,
localRawConnections,
localRawAutoCombos
localRawAutoCombos,
logger
);
const inputWithProvider2 = input as { provider?: Record<string, unknown> };
if (inputWithProvider2.provider) {
Expand Down Expand Up @@ -5623,7 +5645,8 @@ export function createOmniRouteConfigHook(
rawEnrichment,
rawCompressionCombos,
rawConnections,
rawAutoCombos
rawAutoCombos,
logger
);

// Mutate the input.provider map. The Config type declares
Expand Down
Loading
Loading