Skip to content

fix(proxy): make proxy connectors work on non-EPAM tenant model catalogs - #567

Open
vadimvlasenko wants to merge 17 commits into
mainfrom
feature/tenant-model-catalog
Open

vadimvlasenko wants to merge 17 commits into
mainfrom
feature/tenant-model-catalog

Conversation

@vadimvlasenko

Copy link
Copy Markdown
Collaborator

Summary

codemie proxy connect broke on any tenant whose model catalog doesn't follow EPAM's naming convention (surfaced live on a customer call). Three connectors — VS Code Copilot BYOK, Claude Desktop, and VS Code Claude Code — each hardcoded assumptions about model-id shape or format that only held for EPAM's own tenant. Ref: EPMCDME-15073.

Supersedes #566, closed by an accidental branch rename (feature/philips-tenant-model-catalogfeature/tenant-model-catalog, to remove the client name from the branch) — the rename deleted the old ref and GitHub auto-closed the PR instead of migrating it, and the closed PR could not be reopened once its head ref was gone. Same branch content, continued from there.

Changes

  • VS Code Copilot BYOK (vscode.ts, vscode-models.ts, new tenant-catalog.ts): replaced the static 25-model hardcoded list with a capability table intersected against the tenant's live GET /v1/llm_models catalog at connect time; writes the tenant's own model identifier verbatim.
  • Claude Desktop (desktop.ts, new model-name-resolver.ts): preferred-model resolution now handles both EPAM's claude-<family>-<version> and version-first (claude-<version>-<family>) naming, plus undated ids and vendor-prefixed ids (e.g. openai.gpt-...), instead of only appending suffixes.
  • VS Code Claude Code (vscode-claude-code.ts): switched from strict JSON.parse/JSON.stringify to jsonc-parser, so a settings.json with comments or trailing commas parses correctly and the write preserves the file's existing formatting instead of being silently skipped.
  • github-copilot-* deployments are explicitly excluded from both the VS Code Copilot BYOK picker and Claude Desktop (product decision, documented in spec.md).
  • Shared resolver is scoped to the two evidenced naming conventions (not a fully generic solver); codex-model-resolver.ts is untouched.

Full design rationale, acceptance criteria, and non-goals are in docs/superpowers/tasks/2026-09-17-tenant-model-catalog/spec.md and plan.md (committed on this branch), produced by an sdlc-autonomous run with a full code-review cycle (initial round: 7 findings incl. 1 critical, all fixed and independently re-verified in a check round; two additional findings surfaced by a stray duplicate review round were fixed and verified separately, see code-review-final.json).

Testing

  • Tests added/updated — unit tests for the new resolver and tenant-catalog modules, updated connector tests, repaired the two VS Code integration test files broken by the API renames.
  • npx vitest run — full suite green.
  • npm run typecheck / npm run lint — clean.
  • Manual testing against a real non-EPAM tenant — not possible in this environment; covered by synthetic fixtures shaped like the reported tenant's catalog instead (see spec.md's open risks).

Checklist

  • Code follows project standards
  • CI is green (npm run ci) locally via qa-gates
  • No merge conflicts with main
  • CRITICAL check: diff does not touch any X-CodeMie-* attribution header, the Responses API user field, or JWT claim overrides — confirmed, out of scope for this change.

vadimvlasenko and others added 17 commits September 17, 2026 19:00
Generalizes codex-model-resolver.ts's date-stripping identity-parse
shape to strip vendor prefixes (GPT) and match both token orders
(Claude) so VS Code Copilot BYOK and Claude Desktop can resolve
tenant-shaped model ids (philips-tenant-model-catalog task 1).

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
Add fetchTenantModelCatalog and thread a live gateway catalog through
buildManagedModels via resolveTenantModelId, so the capability table
(previous commit) resolves each family to whatever id the tenant
actually registers instead of a fixed EPAM-shaped id. Unmatched
families are dropped; a fully unmatched catalog now rejects with no
file written. gatewayKey is threaded through
writeVsCodeLanguageModelsConfig[AtPath] and connect-orchestrator.ts,
which also reports the real resolved model count instead of the
static table length (philips-tenant-model-catalog tasks 3-4).

Squashed with task 2's vscode-models.ts capability-table rename
(VS_CODE_SUPPORTED_MODELS -> VS_CODE_CAPABILITY_TABLE): the rename's
sole consumers are vscode.ts and connect-orchestrator.ts, so the two
land together to keep every commit typecheck-clean and pre-commit
green, matching this repo's hook that runs `tsc --noEmit` on every
commit.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
Replace selectPreferredClaudeModels's suffix-only exact/dated
matching with the shared resolveTenantModelId, so a tenant that
names models version-first (e.g. claude-5-opus) resolves correctly
instead of never matching PREFERRED_CLAUDE_MODELS's family-first
entries (philips-tenant-model-catalog task 5).

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…Code settings

Read settings.json with jsonc-parser (allowTrailingComma) instead of
strict JSON.parse, and write the two managed keys via targeted
modify()/applyEdits() passes against the original text instead of
re-serializing the whole object, so a hand-edited settings.json with
// comments survives the write (philips-tenant-model-catalog task 6).
A genuinely unparseable file still rejects with ConfigurationError
and is left untouched.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…es by recency

RELEASE_DATE_PATTERN only matched separator-delimited dates, so a
single-number preferred family (claude-opus-5, claude-sonnet-5) never
resolved against a realistically-shaped concatenated-date tenant id
(claude-opus-5-20260101) — two-number families only worked by positional
coincidence. pickMostRecent's lexicographic tie-break was also unsound
across mixed id shapes (vendor-prefixed vs. dated vs. -vertex-suffixed).
Extend the date pattern to cover both shapes and rank same-identity
candidates by an explicit non-vertex-before-vertex, then parsed-date
precedence instead of raw string comparison.

philips-tenant-model-catalog CR-001, CR-002

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…ch fallback

fetchTenantModelCatalog was a ~73-line function mixing fetch, HTTP-status
handling, content-type validation, response-shape parsing, and logging —
over the code-quality function-length/single-responsibility limit. It also
had no timeout on its fetch() call, so a hung gateway blocked `codemie proxy
connect` indefinitely, and its `{ data: [...] }` response branch only read
`m.id`, dropping the same base_name/deployment_name fallback the array
branch already applied.

Extract the parsing and logging into small named helpers, wrap the fetch in
an AbortController-backed timeout (converted into the same ConfigurationError
contract on abort), and apply one shared id/base_name/deployment_name
fallback chain to both response shapes.

philips-tenant-model-catalog CR-003, CR-004, CR-005

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…tings

writeVsCodeClaudeCodeConfigAtPath's two jsonc-parser modify() calls passed
an empty ModificationOptions object, leaving `formattingOptions` undefined.
Per jsonc-parser's own contract that inserts the edit completely
unformatted — no newline, no indent — rather than falling back to any
default, so the managed keys could land jammed onto the same line as the
preceding property. Detect the file's actual indentation and EOL from its
raw text and pass real formattingOptions to both modify() calls.

philips-tenant-model-catalog CR-006

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
Both integration test files still imported the removed
VS_CODE_SUPPORTED_MODELS/VsCodeModelDefinition (renamed to
VS_CODE_CAPABILITY_TABLE/VsCodeCapabilityEntry, whose entries carry a
`family` name instead of a literal tenant id). vscode-byok.test.ts's
writeVsCodeLanguageModelsConfigAtPath call was also missing the new
required gatewayKey argument, and its mock upstream server never served a
/v1/llm_models catalog body, so the connector's catalog fetch found zero
models and the test never exercised its own scenario.

Update both files to the renamed exports and the family-based shape, pass
gatewayKey through, and have the BYOK test's mock server serve a catalog
whose ids exactly match every VS_CODE_CAPABILITY_TABLE family so each entry
resolves verbatim.

philips-tenant-model-catalog CR-007

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
Test fixtures and descriptions named a real customer. Rename them to
describe the tenant-naming shape under test instead: PHILIPS_CLAUDE_FIXTURE
-> VERSION_FIRST_CLAUDE_FIXTURE (desktop.test.ts) and PHILIPS_FIXTURE ->
NON_EPAM_TENANT_FIXTURE (vscode.test.ts), with matching test/describe
titles. No behavior change.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…t-model-catalog

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
state.local.json/gate-plan.local.json replaced .state.json/gate-plan.json
in the sdlc-factory scripts; the ignore patterns here still named the old
filenames, so every finished sdlc run left the task dir looking dirty.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…atches

selectPreferredClaudeModels logged a reordered tenant match (e.g. family
claude-opus-5 resolving to tenant id claude-5-opus) as missing because the
diagnostic re-derived membership from string prefixes instead of asking
resolveTenantModelId directly. Track resolved/missing at the point of
resolution instead. Also drops the now-redundant local -vertex fallback:
resolveTenantModelId's parseClaudeIdentity already strips -vertex before
matching, so the separate string-based fallback could never fire
differently from what the resolver already returns.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
fetchTenantModelCatalog built its request URL before the try block, so a
malformed proxyUrl escaped as a raw TypeError instead of the
ConfigurationError every other failure path in this function guarantees.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…acts

Replace named-customer mentions (Philips, and the reported user's name/
machine path) with generic "non-EPAM tenant" / "reported tenant" phrasing
across the run's technical-analysis, spec, plan, and code-review verdict.
Also reconcile code-review-final.json: a stray duplicate review-triage
dispatch had overwritten it with a 10-item verdict never actually
triaged by the caller. Two of those ten findings were genuinely new
(desktop.ts's false missing-model diagnostic, tenant-catalog.ts's
unwrapped malformed-proxyUrl TypeError); both are now fixed, tested, and
noted as resolved in the file.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
docs/superpowers/tasks/2026-09-17-philips-tenant-model-catalog ->
2026-09-17-tenant-model-catalog. Fixes internal path references in
plan.md's Spec: line and gate-run.json's branch field to match.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>
…mmit

A failed pathspec in the rename commit's git add silently dropped these
two edits (gate-run.json's branch field, plan.md's Spec: path) before
they were committed.

Generated with AI

Co-Authored-By: codemie-ai <[email protected]>

@Filip-Stastny-Philips Filip-Stastny-Philips left a comment

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.

Manual Philips-tenant testing:

  • VS Code Copilot BYOK: ✅
  • Claude Desktop: ✅
  • VS Code Claude Code extension: Configuration succeeds ☑️, but chat requests fail ❌

VS Code Claude extension issue:

  • Philips CodeMie tenant accepts: claude-5-opus (similar for other Claude models)
  • VS Code Claude Code extension sends: claude-opus-5
    The current --vscode-claude-code connector configures only ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN; it does not make the extension’s model catalog tenant-aware or translate canonical model IDs to tenant-specific IDs.

This branch has not been deployed

No deployments
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.

3 participants