Skip to content

fix openrouter aliases and tui key handling#57198

Closed
Mist-wu wants to merge 4 commits into
openclaw:mainfrom
Mist-wu:codex/openrouter-compat-aliases
Closed

fix openrouter aliases and tui key handling#57198
Mist-wu wants to merge 4 commits into
openclaw:mainfrom
Mist-wu:codex/openrouter-compat-aliases

Conversation

@Mist-wu

@Mist-wu Mist-wu commented Mar 29, 2026

Copy link
Copy Markdown

Summary

  • Problem: openrouter:auto and openrouter:free currently fall through the providerless-model path and get rewritten to invalid anthropic/openrouter:* refs; in parallel, pnpm tsgo is broken because two TUI selector components still import removed getKeybindings API from @mariozechner/pi-tui.
  • Why it matters: the OpenRouter shorthand regression breaks configured model selection at runtime, and the TUI import regression blocks the repo's required local commit/check workflow.
  • What changed: added explicit OpenRouter compatibility handling before the anthropic fallback in resolveConfiguredModelRef(), added regression tests for both shorthands, and updated the two TUI selector components to use the current public Key/matchesKey API for cancel handling.
  • What did NOT change (scope boundary): no broader model parsing refactor, no new provider compatibility rules beyond the two OpenRouter shorthands, and no behavioral changes to non-cancel TUI key handling.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause / Regression History (if applicable)

  • Root cause: resolveConfiguredModelRef() handled configured aliases, then immediately applied the legacy anthropic fallback to any remaining providerless ref, so OpenRouter compatibility shorthands never had a chance to resolve; separately, the TUI components still referenced getKeybindings after @mariozechner/pi-tui moved consumers to Key + matchesKey.
  • Missing detection / guardrail: there was no unit coverage for openrouter:auto or openrouter:free, and no type-check guard had been exercised recently against the current pi-tui public exports before local commit validation.
  • Prior context (git blame, prior PR, issue, or refactor if known): closed PR fix: handle openrouter compatibility aliases #57078 proposed the OpenRouter compatibility behavior; issue tui: getKeybindings import no longer exported by @mariozechner/pi-tui, breaking pnpm tsgo #57196 records the TUI type-check break discovered while running the repo's required check chain.
  • Why this regressed now: the providerless fallback path predates the OpenRouter shorthand expectation, and the TUI code lagged behind the upstream pi-tui key API surface.
  • If unknown, what was ruled out: reproduced on current main locally before patching; no evidence of config-specific or environment-specific causation was needed for either failure.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/agents/model-selection.test.ts
  • Scenario the test should lock in: openrouter:auto resolves to openrouter/auto; openrouter:free resolves to the first configured concrete openrouter/...:free model.
  • Why this is the smallest reliable guardrail: the OpenRouter bug is pure resolution logic, and the TUI issue is already covered by pnpm tsgo once the imports use the current public API.
  • Existing test that already covers this (if any): none for the two OpenRouter shorthands.
  • If no new test is added, why not: no dedicated TUI test was added because the import/API mismatch is directly enforced by pnpm tsgo.

User-visible / Behavior Changes

  • openrouter:auto now resolves to canonical openrouter/auto instead of anthropic/openrouter:auto.
  • openrouter:free now resolves to the first configured concrete free OpenRouter model instead of anthropic/openrouter:free.
  • TUI searchable/filterable selectors continue to cancel on Escape/Ctrl+C using the current pi-tui key API.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node via pnpm workspace
  • Model/provider: OpenRouter shorthand refs (openrouter:auto, openrouter:free)
  • Integration/channel (if any): N/A
  • Relevant config (redacted): agents.defaults.model.primary set to the shorthand refs; agents.defaults.models containing concrete openrouter/...:free entries for the free-model case.

Steps

  1. On current main, evaluate resolveConfiguredModelRef() with agents.defaults.model.primary set to openrouter:auto.
  2. Observe it warns and returns {"provider":"anthropic","model":"openrouter:auto"}.
  3. Repeat with openrouter:free and configured concrete free OpenRouter models.
  4. Observe it warns and returns {"provider":"anthropic","model":"openrouter:free"}.
  5. Run pnpm tsgo on current main and observe getKeybindings import failures in the two TUI selector components.
  6. Apply this patch and rerun the same checks.

Expected

  • OpenRouter shorthand refs resolve through OpenRouter compatibility handling.
  • pnpm tsgo passes.

Actual

  • Before this patch, both shorthand refs incorrectly fell back to anthropic and pnpm tsgo failed on removed getKeybindings imports.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: local repro of both OpenRouter shorthand failures before patch; local repro of corrected resolution after patch; pnpm test -- src/agents/model-selection.test.ts -t 'resolveConfiguredModelRef'; pnpm tsgo; pnpm build; full repo check chain via scripts/committer.
  • Edge cases checked: openrouter:free only resolves from configured concrete openrouter/...:free entries; non-cancel TUI key handling was left untouched.
  • What you did not verify: no live OpenRouter API request or interactive manual TUI session was run.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert commit bbd98e25a67b1789a6ba3e76cc3418c3203142ff.
  • Files/config to restore: src/agents/model-selection.ts, src/agents/model-selection.test.ts, src/tui/components/filterable-select-list.ts, src/tui/components/searchable-select-list.ts
  • Known bad symptoms reviewers should watch for: providerless OpenRouter shorthands regressing back to anthropic fallback, or selector cancel keys no longer responding to Escape/Ctrl+C.

Risks and Mitigations

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 29, 2026
@Mist-wu Mist-wu marked this pull request as ready for review March 29, 2026 17:31
Copilot AI review requested due to automatic review settings March 29, 2026 17:31
@Mist-wu Mist-wu changed the title [codex] fix openrouter aliases and tui key handling fix openrouter aliases and tui key handling Mar 29, 2026
@greptile-apps

greptile-apps Bot commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two independent regressions: openrouter:auto and openrouter:free shorthands were incorrectly rewritten to invalid anthropic/openrouter:* refs, and two TUI selector components still imported the removed getKeybindings API from @mariozechner/pi-tui, breaking pnpm tsgo.

Changes:

  • src/agents/model-selection.ts: Adds resolveOpenRouterCompatAlias() that intercepts openrouter:auto/openrouter:free before the legacy anthropic fallback. For auto it returns a canonical openrouter/auto ref; for free it scans configured agents.defaults.models for the first openrouter/...:free entry. Logic and placement in resolveConfiguredModelRef() are correct.
  • src/agents/model-selection.test.ts: Adds regression tests covering both shorthand happy paths.
  • src/tui/components/filterable-select-list.ts and searchable-select-list.ts: Replace the removed getKeybindings() / kb.matches() pattern with the current public Key.escape / Key.ctrl("c") + matchesKey() API — a clean, behaviorally equivalent migration.

Minor concern: When openrouter:free is used as the primary model but no concrete openrouter/...:free entries are configured under agents.defaults.models, resolveOpenRouterCompatAlias returns null and control silently falls through to the legacy anthropic fallback. This produces a misleading warning and an invalid model ref at runtime. A dedicated warning explaining that free models must be configured would improve the user experience for this edge case.

Confidence Score: 5/5

Safe to merge — both fixes are correct, regression tests cover the happy paths, and the only open concerns are P2 style/edge-case quality issues.

All identified issues are P2: one is dead code (redundant String()/?? ""), and the other is a missing user-facing error message for a corner case (openrouter:free with no configured free models). Neither blocks correct behaviour on the documented paths. The core logic is sound, tests are well-placed, and the TUI migration is a straightforward API swap.

src/agents/model-selection.ts — minor cleanup in resolveOpenRouterCompatAlias() and consideration of the openrouter:free-with-no-models error path.

Important Files Changed

Filename Overview
src/agents/model-selection.ts Adds resolveOpenRouterCompatAlias() to handle openrouter:auto and openrouter:free shorthands before the legacy anthropic fallback. Logic is correct for the happy path; the openrouter:free no-configured-models error path silently falls through to a misleading anthropic fallback warning.
src/agents/model-selection.test.ts Adds two regression tests covering openrouter:auto and openrouter:free happy paths. No test for the openrouter:free-with-no-configured-models edge case.
src/tui/components/filterable-select-list.ts Replaces removed getKeybindings() / kb.matches() API with the current public Key.escape / Key.ctrl('c') + matchesKey() pattern. Straightforward and correct.
src/tui/components/searchable-select-list.ts Same getKeybindings() removal as filterable-select-list.ts — clean API migration, no behavioural change to cancel handling.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/model-selection.ts
Line: 288-302

Comment:
**Misleading fallback when `openrouter:free` is used but no free models are configured**

When a user sets their primary model to `openrouter:free` but has not configured any concrete `openrouter/...:free` entries under `agents.defaults.models`, `resolveOpenRouterCompatAlias` returns `null`. Control then falls through to the legacy anthropic fallback, which emits a misleading warning (`"openrouter:free" specified without provider. Falling back to "anthropic/openrouter:free"`) and returns an invalid model ref that will fail at runtime.

Consider emitting a dedicated warning inside `resolveOpenRouterCompatAlias` before returning `null`, telling the user they need at least one `openrouter/...:free` entry configured under `agents.defaults.models`.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/model-selection.ts
Line: 293-294

Comment:
**Redundant defensive wrapping on `Object.keys()` result**

`Object.keys(configuredModels)` always yields plain strings, so `keyRaw` is never null/undefined. Both the `?? ""` fallback and the `String()` cast are unreachable dead code that can be dropped.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: restore openrouter aliases and tui ..." | Re-trigger Greptile

Comment on lines +288 to +302
if (normalized !== "openrouter:free") {
return null;
}

const configuredModels = params.cfg.agents?.defaults?.models ?? {};
for (const keyRaw of Object.keys(configuredModels)) {
const key = String(keyRaw ?? "").trim();
if (!key.startsWith("openrouter/")) {
continue;
}
const parsed = parseModelRef(key, DEFAULT_PROVIDER);
if (parsed?.provider === "openrouter" && parsed.model.endsWith(":free")) {
return parsed;
}
}

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 Misleading fallback when openrouter:free is used but no free models are configured

When a user sets their primary model to openrouter:free but has not configured any concrete openrouter/...:free entries under agents.defaults.models, resolveOpenRouterCompatAlias returns null. Control then falls through to the legacy anthropic fallback, which emits a misleading warning ("openrouter:free" specified without provider. Falling back to "anthropic/openrouter:free") and returns an invalid model ref that will fail at runtime.

Consider emitting a dedicated warning inside resolveOpenRouterCompatAlias before returning null, telling the user they need at least one openrouter/...:free entry configured under agents.defaults.models.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-selection.ts
Line: 288-302

Comment:
**Misleading fallback when `openrouter:free` is used but no free models are configured**

When a user sets their primary model to `openrouter:free` but has not configured any concrete `openrouter/...:free` entries under `agents.defaults.models`, `resolveOpenRouterCompatAlias` returns `null`. Control then falls through to the legacy anthropic fallback, which emits a misleading warning (`"openrouter:free" specified without provider. Falling back to "anthropic/openrouter:free"`) and returns an invalid model ref that will fail at runtime.

Consider emitting a dedicated warning inside `resolveOpenRouterCompatAlias` before returning `null`, telling the user they need at least one `openrouter/...:free` entry configured under `agents.defaults.models`.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +293 to +294
for (const keyRaw of Object.keys(configuredModels)) {
const key = String(keyRaw ?? "").trim();

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 Redundant defensive wrapping on Object.keys() result

Object.keys(configuredModels) always yields plain strings, so keyRaw is never null/undefined. Both the ?? "" fallback and the String() cast are unreachable dead code that can be dropped.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-selection.ts
Line: 293-294

Comment:
**Redundant defensive wrapping on `Object.keys()` result**

`Object.keys(configuredModels)` always yields plain strings, so `keyRaw` is never null/undefined. Both the `?? ""` fallback and the `String()` cast are unreachable dead code that can be dropped.

How can I resolve this? If you propose a fix, please make it concise.

Copilot AI 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.

Pull request overview

Fixes two regressions impacting model resolution and local TUI type-checking: OpenRouter shorthand refs (openrouter:auto, openrouter:free) no longer incorrectly fall through to the legacy Anthropic providerless fallback, and TUI selector components are updated to use the current @mariozechner/pi-tui key API.

Changes:

  • Add explicit OpenRouter compatibility handling for openrouter:auto and openrouter:free before the providerless Anthropic fallback in resolveConfiguredModelRef().
  • Add unit tests covering both OpenRouter shorthands to prevent regression.
  • Replace removed getKeybindings usage in TUI selectors with Key + matchesKey cancel handling.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/agents/model-selection.ts Adds OpenRouter shorthand resolution logic prior to legacy providerless fallback.
src/agents/model-selection.test.ts Adds regression tests for openrouter:auto and openrouter:free.
src/tui/components/filterable-select-list.ts Updates cancel key handling to Key.escape / Key.ctrl("c") using matchesKey.
src/tui/components/searchable-select-list.ts Updates cancel key handling to Key.escape / Key.ctrl("c") using matchesKey.

const configuredModels = params.cfg.agents?.defaults?.models ?? {};
for (const keyRaw of Object.keys(configuredModels)) {
const key = String(keyRaw ?? "").trim();
if (!key.startsWith("openrouter/")) {

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

resolveOpenRouterCompatAlias() filters configured model keys with key.startsWith("openrouter/"), which is case-sensitive. Elsewhere provider IDs are normalized via normalizeProviderId() (lowercased), so configs like OpenRouter/... would be ignored and openrouter:free would incorrectly fall back to the anthropic providerless path. Consider removing the startsWith prefilter (parse every key and check parsed.provider === "openrouter") or at least using a case-insensitive check like key.toLowerCase().startsWith("openrouter/").

Suggested change
if (!key.startsWith("openrouter/")) {
if (!key.toLowerCase().startsWith("openrouter/")) {

Copilot uses AI. Check for mistakes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

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: bbd98e25a6

ℹ️ 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".

const configuredModels = params.cfg.agents?.defaults?.models ?? {};
for (const keyRaw of Object.keys(configuredModels)) {
const key = String(keyRaw ?? "").trim();
if (!key.startsWith("openrouter/")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize OpenRouter keys when resolving openrouter:free

The new compatibility path only considers configured model keys that literally start with "openrouter/", which is case-sensitive. Because provider ids are normalized elsewhere, configs like "OpenRouter/meta-llama/...:free" are valid in other flows but get skipped here, causing openrouter:free to fall through to the anthropic fallback again. Lowercasing before the prefix check (or parsing first and checking parsed.provider) would avoid this regression for mixed-case configs.

Useful? React with 👍 / 👎.

@Mist-wu Mist-wu force-pushed the codex/openrouter-compat-aliases branch from 8685791 to d0621fb Compare March 31, 2026 01:11
@openclaw-barnacle openclaw-barnacle Bot added app: macos App: macos size: S and removed size: M labels Mar 31, 2026
@Mist-wu Mist-wu closed this by deleting the head repository Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: macos App: macos size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: OpenRouter compatibility aliases: openrouter:free resolves to anthropic/openrouter:free; openrouter:auto should also be handled explicitly

2 participants