Skip to content

fix(models): resolve provider-qualified aliases#100209

Merged
steipete merged 2 commits into
mainfrom
codex/fix-provider-qualified-model-alias
Jul 5, 2026
Merged

fix(models): resolve provider-qualified aliases#100209
steipete merged 2 commits into
mainfrom
codex/fix-provider-qualified-model-alias

Conversation

@steipete

@steipete steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Closes #75163

Reworks the useful direction from #75198 on current main, with provider-scoped duplicate handling and contributor credit preserved.

What Problem This Solves

Fixes an issue where users switching an existing Control UI or TUI session to a provider-qualified display alias such as lmstudio-moe/MoE would persist MoE as the model id. Local providers then rejected the next turn because the display alias was not their canonical model identifier.

Why This Change Was Made

The shared model resolver now indexes aliases by provider as well as by bare display name, so provider/Alias resolves without colliding when two providers use the same alias. Chat-command model selection now delegates to that same resolver instead of maintaining a second parser.

User Impact

Mid-session model switches from Control UI, TUI, and model commands now persist the canonical provider/model reference. Existing bare-alias behavior remains unchanged.

Evidence

  • node scripts/run-vitest.mjs src/agents/model-selection.test.ts — 121 tests passed.
  • node scripts/run-vitest.mjs src/gateway/sessions-patch.test.ts — 228 tests passed across the routed Gateway shards.
  • node scripts/run-vitest.mjs src/auto-reply/reply/model-selection-directive.test.ts — focused chat-command regression passed.
  • Testbox-through-Crabbox tbx_01kwrjhsjn8geh91q5w6rnh5j3: pnpm check:changed passed, including core and test typechecks, changed-file lint, guards, and import-cycle checks.
  • Real tmux TUI against a live Gateway: /model local-a/Local rendered model set to local-a/model-a; sessions.list confirmed modelProvider=local-a, model=model-a.
  • Real tmux tui --local: the same command rendered and persisted local-a/model-a.
  • .agents/skills/autoreview/scripts/autoreview --mode local --stream-engine-output — clean, no accepted/actionable findings.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime agents Agent runtime and tooling size: S maintainer Maintainer-authored PR labels Jul 5, 2026
@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 5, 2026, 9:43 AM ET / 13:43 UTC.

Summary
The PR adds provider-scoped alias indexing to the shared model resolver, routes chat-command model directives through that resolver, and adds resolver/session-patch regression coverage.

PR surface: Source -12, Tests +115. Total +103 across 5 files.

Reproducibility: yes. Source inspection shows current main routes sessions.patch model updates through the shared resolver, which only checks the whole provider/Alias string against bare aliases before direct provider/model parsing.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: src/gateway/sessions-patch.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #75163
Summary: This PR is the current candidate fix for the canonical Control UI/TUI provider-qualified alias canonicalization bug; the earlier candidate PR was closed unmerged as superseded.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Risk before merge

  • [P1] Provider-qualified alias inputs now resolve through the alias table before direct provider/model parsing; if a provider has a real model id with the same suffix as a configured alias, maintainers should accept that precedence intentionally.
  • [P1] The PR changes session model persistence and chat-command model selection through a shared resolver, so upgrades should rely on the added regression tests and exact-head checks rather than assuming this is isolated to one UI path.
  • [P1] Live PR checks showed normal skipped/cancelled bot jobs and one pending QA Smoke CI entry during review; this is not a patch defect, but merge should still wait for required exact-head checks.

Maintainer options:

  1. Accept provider-scoped alias precedence (recommended)
    Merge after required checks finish if maintainers agree that provider/Alias should resolve through configured aliases before direct parsing.
  2. Add a direct-model collision rule
    Before merge, change the resolver order or add an explicit collision test if direct provider/model ids should remain stronger than aliases.
  3. Pause for the broader Control UI picker cluster
    Keep this PR open or close it only if maintainers want the canonical issue solved by a broader picker/session reconciliation design instead.

Next step before merge

  • [P2] Protected maintainer label plus compatibility-sensitive resolver precedence leaves maintainer acceptance/merge as the remaining action, not an automated repair.

Maintainer decision needed

  • Question: Should this PR land with provider-qualified aliases taking precedence over same-provider direct model ids when the suffix matches a configured alias?
  • Rationale: The diff fixes the canonical bug at the shared resolver boundary, but that precedence is a compatibility-sensitive model-routing choice that automation should not silently own.
  • Likely owner: steipete — He has the strongest history across model aliases, shared model-selection helpers, and sessions.patch model override behavior.
  • Options:
    • Accept alias precedence (recommended): Merge once required checks finish, treating provider/Alias as an intentional alias form when the provider matches the alias target.
    • Require direct-id precedence: Ask for a resolver-order change and regression test if direct provider/model ids must always beat aliases with the same suffix.
    • Pause for broader picker policy: Hold this PR if maintainers want to solve all Control UI display-value canonicalization cases together instead of landing the alias-specific fix now.

Security
Cleared: No concrete security or supply-chain concern was found; the diff is limited to model resolution logic and tests, with no dependency, workflow, secret, or execution-surface change.

Review details

Best possible solution:

Land the shared resolver fix after maintainer acceptance of provider-scoped alias precedence and required exact-head checks/proof remain green.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main routes sessions.patch model updates through the shared resolver, which only checks the whole provider/Alias string against bare aliases before direct provider/model parsing.

Is this the best way to solve the issue?

Yes. The shared resolver is the right layer because sessions.patch, model commands, and other model-selection surfaces already depend on it; a UI-only or provider-fallback fix would be too narrow.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 4c42815348ae.

Label changes

Label changes:

  • add merge-risk: 🚨 compatibility: The resolver precedence for provider/model-looking strings changes when the model suffix matches a configured alias.
  • add merge-risk: 🚨 session-state: sessions.patch persists the resolved provider/model override onto existing sessions, so a wrong resolution would stick to session state.
  • add merge-risk: 🚨 auth-provider: The changed resolver affects provider/model choice before provider routing and auth-profile behavior are applied.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR includes after-fix live terminal proof for Gateway TUI and tui --local persistence, and the final runtime diff matches that proof aside from removing the earlier changelog edit.
  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.

Label justifications:

  • P2: The PR fixes a bounded but user-visible model-switching bug without evidence of core runtime unavailability or data loss.
  • merge-risk: 🚨 compatibility: The resolver precedence for provider/model-looking strings changes when the model suffix matches a configured alias.
  • merge-risk: 🚨 session-state: sessions.patch persists the resolved provider/model override onto existing sessions, so a wrong resolution would stick to session state.
  • merge-risk: 🚨 auth-provider: The changed resolver affects provider/model choice before provider routing and auth-profile behavior are applied.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR includes after-fix live terminal proof for Gateway TUI and tui --local persistence, and the final runtime diff matches that proof aside from removing the earlier changelog edit.
Evidence reviewed

PR surface:

Source -12, Tests +115. Total +103 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 29 41 -12
Tests 3 115 0 +115
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 144 41 +103

What I checked:

  • Current-main resolver gap: Current main checks the whole raw model string against bare aliases and then parses it as provider/model, so a value like provider/Alias does not resolve through the bare Alias entry first. (src/agents/model-selection-shared.ts:712, 4c42815348ae)
  • Current-main session patch boundary: sessions.patch strips any auth profile suffix, calls resolveAllowedModelRef, and persists the resolved provider/model override into the session entry, making alias canonicalization a session-state and provider-routing boundary. (src/gateway/sessions-patch.ts:574, 4c42815348ae)
  • PR resolver implementation: The compare shows the PR adds a provider alias map and resolves provider-qualified alias keys before falling through to normal provider/model parsing. (src/agents/model-selection-shared.ts:46, 196edd642a9c)
  • Regression coverage: The PR adds provider-qualified duplicate-alias tests for the shared resolver, chat directive resolver, and gateway sessions.patch persistence path. (src/gateway/sessions-patch.test.ts:598, 196edd642a9c)
  • Real behavior proof: The PR body and follow-up comment report live tmux Gateway TUI and tui --local runs where /model local-a/Local rendered and persisted local-a/model-a; latest head only removes the earlier changelog entry beyond the runtime fix. (196edd642a9c)
  • Canonical related work: The linked issue remains the canonical open bug, while the older contributor PR for the same provider-qualified alias problem was closed unmerged as superseded by this newer path.

Likely related people:

  • steipete: Introduced model aliases, extracted shared model-selection helpers, and centralized sessions model override validation on the paths this PR changes. (role: feature-history contributor and likely follow-up owner; confidence: high; commits: 5c8e1b6eeff8, 310d2db31241, 7e81980747b3; files: src/agents/model-selection-shared.ts, src/agents/model-selection.ts, src/gateway/sessions-patch.ts)
  • vincentkoc: Recent current-main history touches the shared model-selection and sessions-patch files in the release/package baseline inspected for this review. (role: recent area contributor; confidence: medium; commits: e085fa1a3ffd, c22270a9179f; files: src/agents/model-selection-shared.ts, src/gateway/sessions-patch.ts, src/auto-reply/reply/model-selection-directive.ts)
  • sahilsatralkar: Authored the earlier closed candidate PR for the same provider-qualified alias issue and is credited as co-author on the current runtime fix commit. (role: source proposal contributor; confidence: medium; commits: c1032904d0af, 7ce218c17c00; files: src/agents/model-selection-shared.ts, src/gateway/sessions-patch.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (4 earlier review cycles)
  • reviewed 2026-07-05T07:36:32.541Z sha ecb2b52 :: found issues before merge. :: [P3] Remove the release-owned changelog edit
  • reviewed 2026-07-05T07:44:31.493Z sha ecb2b52 :: found issues before merge. :: [P3] Remove the release-owned changelog entry
  • reviewed 2026-07-05T11:19:52.680Z sha 719a7c2 :: needs changes before merge. :: [P3] Remove the release-owned changelog entry
  • reviewed 2026-07-05T13:31:01.007Z sha 7a5cfa6 :: needs maintainer review before merge. :: none

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ecb2b524bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CHANGELOG.md Outdated

### Fixes

- **Model aliases:** resolve provider-qualified aliases during session and chat-command model switches without collisions when providers share a display alias. Thanks @sahilsatralkar.

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.

P2 Badge Remove normal-fix changelog entry

For this non-release model fix, the root AGENTS.md says CHANGELOG.md is release-only and release generation owns it; adding this entry directly can leave stale release-note content/credits or create avoidable release conflicts. Please keep the release-note context in the commit/PR body and leave CHANGELOG.md unchanged here.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 5, 2026
@steipete

steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Land-ready proof for synchronized head 196edd642a9c12b76960500ce1eb97894f94b1ca:

  • node scripts/run-vitest.mjs src/agents/model-selection.test.ts — 121 passed.
  • node scripts/run-vitest.mjs src/gateway/sessions-patch.test.ts — 228 passed across routed Gateway shards.
  • node scripts/run-vitest.mjs src/auto-reply/reply/model-selection-directive.test.ts — focused regression passed.
  • Blacksmith Testbox tbx_01kwrjhsjn8geh91q5w6rnh5j3pnpm check:changed passed, including production/test typechecks, changed-file lint, guards, and import-cycle checks.
  • Real tmux Gateway TUI — /model local-a/Local rendered model set to local-a/model-a; sessions.list persisted modelProvider=local-a, model=model-a.
  • Real tmux tui --local — the same command rendered and persisted local-a/model-a.
  • Fresh Codex autoreview — no accepted/actionable findings.
  • Exact-head GitHub CI — all meaningful checks green after rebasing onto the repaired current main baseline.

The rewrite keeps one shared resolver for session patches and chat directives, handles duplicate display aliases within provider scope, and preserves existing bare-alias behavior.

The required changelog entry and contributor credit are being aggregated into the final maintainer changelog commit on #91002, avoiding repeated runtime-CI invalidation from unrelated CHANGELOG.md churn on busy main.

@steipete
steipete force-pushed the codex/fix-provider-qualified-model-alias branch 2 times, most recently from 6bb0db5 to 719a7c2 Compare July 5, 2026 11:11
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 5, 2026
@steipete
steipete force-pushed the codex/fix-provider-qualified-model-alias branch from 719a7c2 to bae5d0f Compare July 5, 2026 13:10
@openclaw-barnacle openclaw-barnacle Bot added channel: voice-call Channel integration: voice-call app: web-ui App: web-ui size: M and removed size: S labels Jul 5, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 5, 2026
steipete and others added 2 commits July 5, 2026 09:35
Co-authored-by: Sahil Satralkar <62758655+sahilsatralkar@users.noreply.github.com>
@steipete
steipete force-pushed the codex/fix-provider-qualified-model-alias branch from 7a5cfa6 to 196edd6 Compare July 5, 2026 13:35
@clawsweeper clawsweeper Bot removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 5, 2026
@steipete
steipete merged commit ba821e7 into main Jul 5, 2026
105 of 115 checks passed
@steipete
steipete deleted the codex/fix-provider-qualified-model-alias branch July 5, 2026 13:44
@steipete

steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
* fix(models): resolve provider-qualified aliases

Co-authored-by: Sahil Satralkar <62758655+sahilsatralkar@users.noreply.github.com>

* docs(changelog): defer TUI batch entries

---------

Co-authored-by: Sahil Satralkar <62758655+sahilsatralkar@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling gateway Gateway runtime maintainer Maintainer-authored PR P2 Normal backlog priority with limited blast radius. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: TUI mid-session model switch passes raw alias instead of resolved model ID

1 participant