Skip to content

Release v1.2.2 — Model metadata overrides fix + Agent guidelines consolidation#26

Merged
Alph4d0g merged 6 commits into
mainfrom
release/v1.2.2
May 25, 2026
Merged

Release v1.2.2 — Model metadata overrides fix + Agent guidelines consolidation#26
Alph4d0g merged 6 commits into
mainfrom
release/v1.2.2

Conversation

@Alph4d0g
Copy link
Copy Markdown
Owner

Summary

This release includes the model metadata override fix from PR #25 and consolidates the project's agent guidelines documentation.

Changes

Fixes

  • Model Metadata Overrides for Provider Models ()
    • Fixed model metadata overrides not being applied to provider models returned by the hook. Previously, user-configured metadata overrides (via ) were only applied to models fetched from , but not to the provider models exposed through the OpenCode >=1.14.49 model listing API. Now overrides are correctly merged into all model paths.
    • Added comprehensive regression tests covering: metadata override application, override precedence, partial override merging, and provider model hook integration.

Documentation

  • Consolidated Agent Guidelines — Merged and into a single canonical file. is now a symlink to so both Claude Code and other agents read from the same source.
  • Release Process Documentation — Added complete release process steps to (version bump, changelog, tag, GitHub release, npm publish, verification).

Version Bump

  • Bumped version to
  • Updated with release notes

Files Changed

File Change
Apply metadata overrides to provider models; add helper functions
Add regression tests for metadata override behavior
Consolidated and expanded agent guidelines
Replaced with symlink to
Added v1.2.2 release notes
Version bump to 1.2.2

Checklist

  • Version bumped in
  • updated
  • All tests pass (

opencode-omniroute-auth@1.2.2 test
npm run build && node --test test/*.test.mjs

opencode-omniroute-auth@1.2.2 build
tsc

✔ warn() writes to log file with correct format (54.280125ms)
✔ debug() writes when OMNIROUTE_DEBUG=1 (52.285042ms)
✔ debug() does not write when OMNIROUTE_DEBUG is not set (0.62025ms)
✔ debug() does not write when OMNIROUTE_DEBUG is "true" (0.399916ms)
✔ debug() does not write when OMNIROUTE_DEBUG is "0" (0.386333ms)
✔ warn() always writes regardless of OMNIROUTE_DEBUG (51.083875ms)
✔ logger handles missing log directory gracefully (0.47875ms)
✔ logger handles log file rotation (103.131833ms)
✔ logger re-scans when no log file exists at module load (51.29625ms)
✔ logger silently skips on non-ENOENT write errors (0.980125ms)
✔ logger silently skips on unreadable log directory (0.832417ms)
✔ logger excludes directories with .log suffix (51.553334ms)
✔ logger uses alphabetical tie-breaker for identical mtime (67.687375ms)
✔ fresh cache hit - no second network call (25.147208ms)
✔ timeout then success - retries and returns data (251.851583ms)
✔ retryable HTTP failure then success - retries 503 (253.456875ms)
✔ all attempts fail with stale cache available - returns stale data (756.827166ms)
✔ all attempts fail with no cache - returns null (754.300708ms)
✔ non-retryable HTTP failure - fail fast on 404 (1.678917ms)
✔ invalid response structure with stale cache - returns stale data (1.785667ms)
✔ invalid provider structure with no cache - returns null (1.151834ms)
✔ getModelsDevIndex integrates with retry and cache (254.006709ms)
✔ fetchModels caches successful responses (25.160917ms)
✔ refreshModels forces refetch (0.8555ms)
✔ fetchModels falls back to defaults when response shape is invalid (0.272542ms)
✔ calculateLowestCommonCapabilities ignores missing attachment metadata (0.1155ms)
✔ calculateLowestCommonCapabilities respects explicit attachment false (0.087375ms)
✔ fetchModels uses different cache for different modelsDev configs (0.540083ms)
✔ calculateLowestCommonCapabilities produces identical output for single model and combo-with-self (0.094333ms)
✔ calculateLowestCommonCapabilities handles mixed defined and undefined temperature (0.053666ms)
✔ calculateLowestCommonCapabilities handles explicit temperature false overriding true (0.054791ms)
✔ calculateLowestCommonCapabilities handles all three capabilities together (0.073708ms)
✔ calculateLowestCommonCapabilities handles single model with undefined temperature (0.042792ms)
✔ variant suffix stripping works with alias resolution end-to-end (0.205708ms)
✔ subscription provider fallback enriches from public provider (0.073375ms)
✔ groupVariantModels merges capability flags from all variants into synthetic base (0.076ms)
✔ normalizeModel reads snake_case fields (0.278ms)
✔ normalizeModel prefers camelCase over snake_case (0.231458ms)
✔ deduplication removes alias when canonical exists (0.231834ms)
✔ deduplication keeps alias when canonical is missing (0.218333ms)
✔ config hook applies defaults and normalized apiMode (228.582292ms)
✔ loader injects auth headers only for OmniRoute URLs (1.33375ms)
✔ auth loader applies user modelMetadata override to provider models (0.49375ms)
✔ gemini tool schema payload is sanitized before forwarding (0.580584ms)
✔ non-gemini payload keeps original tool schema fields (0.343167ms)
✔ gemini schema sanitization applies to responses endpoint request objects (759.074416ms)
✔ provider hook fetches models when auth is available via context (1.897583ms)
✔ provider hook applies modelMetadata overrides before converting models (1.238333ms)
✔ provider hook applies array literal alias block to canonical fetched model (1.49275ms)
✔ provider hook treats string metadata match as a literal model id (1.158625ms)
✔ provider hook addIfMissing array block creates canonical missing model (1.188333ms)
✔ provider hook ignores generated modelMetadata from config hook (12.51975ms)
✔ provider hook uses raw user modelMetadata after config hook generated metadata (5.505042ms)
✔ provider hook uses RegExp raw modelMetadata after config hook JSON clone (3.841375ms)
✔ auth loader uses raw user modelMetadata after config hook generated metadata (3.553542ms)
✔ provider hook ignores stale provider.models and returns defaults when no auth available (0.22875ms)
✔ provider hook returns defaults when fetch fails (fetchModels handles errors) (0.259916ms)
✔ config hook eagerly fetches models when auth is available (2.565792ms)
✔ config hook refreshes plugin-generated models on second run (8.791625ms)
✔ config hook refreshes legacy generated provider models without marker (2.5305ms)
✔ config hook preserves explicit user provider models (2.399625ms)
✔ config hook preserves user modelMetadata object overrides (7.972458ms)
✔ config hook preserves user modelMetadata match blocks (2.044875ms)
✔ config hook respects explicit attachment false for vision models (2.769541ms)
✔ provider hook groups variant models under base model (0.503875ms)
✔ provider hook creates synthetic base model when only variants are returned (0.325875ms)
ℹ tests 66
ℹ suites 0
ℹ pass 66
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2387.720333)

  • Build succeeds (

opencode-omniroute-auth@1.2.2 build
tsc)

  • Export validation passes (

opencode-omniroute-auth@1.2.2 check:exports
node --input-type=module -e "import('./dist/index.js').then((m)=>{if(typeof m.default!=='function'){throw new Error('Default export must be a function');}for(const [key,value] of Object.entries(m)){if(typeof value!=='function'){throw new Error('Root export '+key+' must be a function to satisfy plugin loader constraints');}}})")

Post-Merge Steps

After merging this PR:

  1. Tag the release:
  2. Push the tag:
  3. Create GitHub release: (or use CHANGELOG excerpt)
  4. Publish to npm:

opencode-omniroute-auth@1.2.2 prepublishOnly
npm run clean && npm run build && npm run check:exports

opencode-omniroute-auth@1.2.2 clean
node -e "const fs=require('fs');if(fs.existsSync('dist'))fs.rmSync('dist',{recursive:true});"

opencode-omniroute-auth@1.2.2 build
tsc

opencode-omniroute-auth@1.2.2 check:exports
node --input-type=module -e "import('./dist/index.js').then((m)=>{if(typeof m.default!=='function'){throw new Error('Default export must be a function');}for(const [key,value] of Object.entries(m)){if(typeof value!=='function'){throw new Error('Root export '+key+' must be a function to satisfy plugin loader constraints');}}})"

opencode-omniroute-auth@1.2.2 prepublishOnly
npm run clean && npm run build && npm run check:exports

opencode-omniroute-auth@1.2.2 clean
node -e "const fs=require('fs');if(fs.existsSync('dist'))fs.rmSync('dist',{recursive:true});"

opencode-omniroute-auth@1.2.2 build
tsc

opencode-omniroute-auth@1.2.2 check:exports
node --input-type=module -e "import('./dist/index.js').then((m)=>{if(typeof m.default!=='function'){throw new Error('Default export must be a function');}for(const [key,value] of Object.entries(m)){if(typeof value!=='function'){throw new Error('Root export '+key+' must be a function to satisfy plugin loader constraints');}}})"

abien and others added 4 commits May 21, 2026 14:46
* fix: apply model metadata overrides to provider models

* test: isolate auth store XDG data home

* fix: treat string metadata matches as literals

* refactor: centralize metadata extraction keys

* fix: refresh legacy generated provider models

---------

Co-authored-by: Sebastian Rumpf <alp4d0g007@googlemail.com>
- Rewrite AGENTS.md by cherry-picking the best of AGENTS.md and CLAUDE.md:
  - Keep generic Agent Guidelines framing and all code style rules
  - Add Architecture section (dual entry points, core modules, fetch interceptor, caching)
  - Expand Common Commands (test runner, check:exports, single-file typecheck)
  - Add Testing, Important Configuration, and full Release Process sections
- Delete CLAUDE.md and replace with symlink to AGENTS.md so both Claude Code
  and other agents read from the same canonical source.
Brings in PR #25 fixes (model metadata overrides to provider models)
alongside the local AGENTS.md consolidation and CLAUDE.md symlink.
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 22, 2026

Code Review Summary

Verdict: No Issues Found | Recommendation: Merge

Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything.

📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are.

Files Reviewed (1 file)
  • src/plugin.ts — Performance optimization to applyModelMetadataOverrides function

Incremental Review Notes

Changes since last review (commit 3c69dd7..27b010a):

The single incremental commit (27b010a - "perf: pre-process metadata blocks to avoid redundant regex/alias calculations") optimizes the applyModelMetadataOverrides function:

  1. Pre-processes metadata blocks once before the model iteration loop, extracting canonicalMatch and metadata upfront
  2. Adds processedBlockMatches helper for cleaner matching logic
  3. Eliminates redundant resolveProviderAliasForMetadata and extractModelMetadata calls that were previously inside models.map

This directly addresses the previous review comment about performance (redundant coerceRegExp and resolveProviderAliasForMetadata calls). The optimization is well-implemented and improves the O(N × M) complexity situation noted in the original feedback.

Previously noted issue (getApiMode warn() at line 269) remains unchanged but is outside this diff and noted as pre-existing in the prior review.

Reviewed by: Kilo (kilo-auto/free)


Reviewed by laguna-m.1-20260312:free · 2,093,213 tokens

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request consolidates agent guidelines into AGENTS.md, documents the release process, and fixes an issue where model metadata overrides were not correctly applied to provider models. It introduces a mechanism to track raw user-authored metadata separately from generated metadata using symbols and internal options, ensuring user overrides persist across plugin lifecycle hooks. New logic in src/plugin.ts allows for complex metadata overrides using literal IDs or regex patterns, including the ability to add missing models. Feedback highlights performance concerns in the array-based override logic due to redundant calculations and regex re-creation within loops. It also identifies a logging issue with serialized regex objects and recommends pre-processing configuration blocks. Additionally, a reminder was provided to ensure model capability flags are merged using a logical OR when grouping variants.

Comment thread src/plugin.ts
Comment on lines +534 to +579
if (Array.isArray(userConfig)) {
const validBlocks = userConfig.filter((block) => {
const validation = isValidModelMetadata(block);
if (!validation.valid) {
warn(`Invalid metadata block for match "${sanitizeForLog(String(block.match))}" (field: ${sanitizeForLog(validation.field ?? '')}), skipping`);
return false;
}
return true;
});

const modelsWithOverrides = models.map((model) => {
const canonicalId = resolveProviderAliasForMetadata(model.id);
const block = validBlocks.find((candidate) =>
metadataBlockMatches(candidate.match, model.id, canonicalId),
);
if (!block) return model;

return {
...model,
...extractModelMetadata(block),
};
});

const existingModels = modelsWithOverrides.map((model) => ({
id: model.id,
canonicalId: resolveProviderAliasForMetadata(model.id),
}));
const missingModels: OmniRouteModel[] = [];
for (const block of validBlocks) {
if (block.addIfMissing !== true || typeof block.match !== 'string') continue;

const id = resolveProviderAliasForMetadata(block.match);
const alreadyExists = existingModels.some((model) =>
metadataBlockMatches(block.match, model.id, model.canonicalId),
) || missingModels.some((model) => model.id === id);
if (alreadyExists) continue;

missingModels.push({
id,
name: block.name ?? id,
...extractModelMetadata(block),
});
}

return [...modelsWithOverrides, ...missingModels];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The array-based override logic performs redundant calculations inside the models.map loop. Specifically, resolveProviderAliasForMetadata and coerceRegExp are called for every model-block pair (O(N * M)). If the configuration was serialized (e.g., during an OpenCode lifecycle hook), coerceRegExp will re-create RegExp objects on every call. Additionally, String(block.match) at line 538 will log "[object Object]" for serialized regex objects.

Consider pre-processing userConfig into a list of processedBlocks that include pre-compiled regexps and pre-calculated canonical match strings. This will improve performance, which is important as this logic runs in the provider.models hook. Furthermore, ensure that when grouping model variants under a base model, all capability flags (such as vision, tools, streaming, temperature, and attachment support) are merged using a logical OR (union) so the base model accurately reflects the collective capabilities available across its variants.

References
  1. When grouping model variants under a base model, ensure that all capability flags (such as vision, tools, streaming, temperature, and attachment support) are merged using a logical OR (union) so the base model accurately reflects the collective capabilities available across its variants.

Fixes pre-existing indentation issue flagged by kilo-code-bot review.
No functional change.
@Alph4d0g
Copy link
Copy Markdown
Owner Author

Fixed the pre-existing indentation issue in flagged by @kilo-code-bot. The call was over-indented (4 spaces instead of 2). No functional change — purely formatting.

Commit:

…ulations

Addresses gemini-code-assist review feedback on PR #26:
- Pre-process array-based metadata blocks once: canonicalize string matches,
  compile regexes, and extract metadata upfront.
- Replaces O(N*M) redundant resolveProviderAliasForMetadata + coerceRegExp
  calls inside per-model loops with a single pass over config blocks.
- Adds processedBlockMatches() helper that uses pre-computed canonicalMatch
  instead of re-resolving aliases on every comparison.
- No functional change — all 66 tests pass.
@Alph4d0g
Copy link
Copy Markdown
Owner Author

@gemini-code-assist Thanks for the review! I've addressed the performance concern in the array-based override logic (lines 534–579):

What changed:

  • Pre-processed blocks: Instead of calling and inside the per-model loops (O(N×M)), blocks are now pre-processed in a single pass before any model iteration.
  • Pre-computed canonical matches: String matches get their canonical form resolved once and stored in . Regex matches are compiled once via during pre-processing.
  • New helper: Uses the pre-computed directly instead of re-resolving aliases on every comparison.
  • Extracted metadata once: is called during pre-processing, not repeatedly inside loops.

Result: The inner loops are now simple lookups/comparisons with no redundant calculations. All 66 tests pass.

Commit: 27b010a

@gemini-code-assist
Copy link
Copy Markdown
Contributor

@Alph4d0g Thank you for the detailed breakdown of the performance optimizations. Moving the pre-processing logic outside of the per-model loops is a significant improvement, and the use of pre-compiled Regex and canonicalized string lookups is exactly the right approach for this type of metadata enrichment. The added regression tests also provide great coverage for these changes. The implementation looks solid and addresses the concerns raised. Great work on this PR.

@Alph4d0g Alph4d0g merged commit b23df26 into main May 25, 2026
3 checks passed
@Alph4d0g Alph4d0g deleted the release/v1.2.2 branch May 25, 2026 10:44
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.

2 participants