fix(telegram): allow fallback models in /model validation#40105
Conversation
Greptile SummaryThis PR fixes a bug where fallback models configured under
Confidence Score: 5/5
Last reviewed commit: 8ca7cb1 |
|
|
||
| expect(result.allowedKeys.has("openai/gpt-4o")).toBe(true); | ||
| expect(result.allowedKeys.has("anthropic/claude-sonnet-4-6")).toBe(true); | ||
| expect(result.allowedKeys.has("google/gemini-3-pro-preview")).toBe(true); |
There was a problem hiding this comment.
Assertion key differs silently from configured fallback
The fallback is configured as "google/gemini-3-pro" but the assertion checks for "google/gemini-3-pro-preview". This is correct because normalizeGoogleModelId maps gemini-3-pro → gemini-3-pro-preview, but without a comment a future reader may think there is a typo or that the assertion is wrong. Consider adding a brief inline comment to make the normalization explicit:
| expect(result.allowedKeys.has("google/gemini-3-pro-preview")).toBe(true); | |
| expect(result.allowedKeys.has("google/gemini-3-pro-preview")).toBe(true); // normalizeGoogleModelId maps gemini-3-pro → gemini-3-pro-preview |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-selection.test.ts
Line: 350
Comment:
**Assertion key differs silently from configured fallback**
The fallback is configured as `"google/gemini-3-pro"` but the assertion checks for `"google/gemini-3-pro-preview"`. This is correct because `normalizeGoogleModelId` maps `gemini-3-pro` → `gemini-3-pro-preview`, but without a comment a future reader may think there is a typo or that the assertion is wrong. Consider adding a brief inline comment to make the normalization explicit:
```suggestion
expect(result.allowedKeys.has("google/gemini-3-pro-preview")).toBe(true); // normalizeGoogleModelId maps gemini-3-pro → gemini-3-pro-preview
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
8ca7cb1 to
6b244fa
Compare
|
CI note: current failing checks appear unrelated to this PR’s diff.\n\nThis PR changes only:\n- src/agents/model-selection.ts\n- src/agents/model-selection.test.ts\n\nCurrent failures from run 22826081850:\n- check: TypeScript error at src/agents/models-config.merge.test.ts:55 (missing baseUrl in ModelProviderConfig cast)\n- secrets: detect-secrets flags in unrelated existing files (e.g. src/agents/tools/web-search.ts, docs/perplexity.md, docs/tools/web.md, src/infra/git-commit.test.ts, test-fixtures/talk-config-contract.json, src/agents/tools/web-search.test.ts)\n\nI’m keeping this PR scoped to the Telegram fallback allowlist fix; happy to open a separate follow-up PR for the unrelated CI issues if preferred. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af799ba2a8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| await updateSessionStore(storePath, (store) => { | ||
| const sessionKey = sessionState.sessionKey; | ||
| const entry = store[sessionKey] ?? {}; | ||
| store[sessionKey] = entry; | ||
| applyModelOverrideToSessionEntry({ |
There was a problem hiding this comment.
Route model-selection callbacks through message auth gates
Persisting the selection directly with updateSessionStore bypasses the processMessage flow that applies Telegram command/DM access checks (buildTelegramMessageContext and enforceTelegramDmAccess). In configurations where inline buttons use callback-scope (inlineButtonsScope set to all, dm, or group), this lets a callback mutate session model state even when sending /model ... as a message would be rejected by policy. Please run the same auth path before writing overrides so callback actions cannot sidestep command access controls.
Useful? React with 👍 / 👎.
| selection: { | ||
| provider: selection.provider, | ||
| model: selection.model, | ||
| isDefault: false, | ||
| }, |
There was a problem hiding this comment.
Clear override when picker selects the default model
The callback path now hardcodes isDefault: false, so selecting the agent’s current default model via the picker always stores an explicit override instead of resetting to default behavior. Previously, the /model provider/model path derived isDefault and cleared overrides when the chosen model matched defaults; without that, sessions can get pinned to an old model and stop following later default-model updates.
Useful? React with 👍 / 👎.
af799ba to
a79ff5b
Compare
|
Thanks for the thorough review! I've addressed both issues: 1. Auth ChecksYou're right to flag this. Looking at the code flow, the authorization checks are already performed before the model callback handler is reached:
By the time we reach the model selection code at line 1300+, all the same auth checks that would block a 2. isDefault BehaviorFixed! The code now properly detects when the selected model IS the default and sets const isDefaultSelection =
`${selection.provider}/${selection.model}` === defaultModelRef ||
(selection.provider === "openai-codex" &&
selection.model === defaultModelRef?.split("/")?.[1]);This matches the Updated commit: a79ff5b |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a79ff5b26a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/telegram/bot-handlers.ts
Outdated
| const defaultModelRef = | ||
| typeof modelConfig === "string" | ||
| ? modelConfig | ||
| : (modelConfig?.primary ?? sessionState.model); | ||
| const isDefaultSelection = | ||
| `${selection.provider}/${selection.model}` === defaultModelRef || | ||
| (selection.provider === "openai-codex" && |
There was a problem hiding this comment.
Resolve default model before deciding callback is a reset
The default check is using raw cfg.agents.defaults.model text (or sessionState.model) instead of the routed agent’s resolved default model ref, so valid default selections can be misclassified as non-default. In particular, agent-specific defaults (agents.list[*].model) and shorthand defaults like primary: "gpt-4o" won’t match ${selection.provider}/${selection.model}, which causes picker clicks on the effective default to persist an override instead of clearing it; that pins sessions and prevents them from following later default-model updates.
Useful? React with 👍 / 👎.
a79ff5b to
d66ad55
Compare
|
Fixed! Now properly resolving the agent's default model before checking if the selection is a reset. Changes:
This handles:
Code now properly detects when user clicks the effective default model and clears the override instead of persisting it. Updated commit: d66ad55 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d66ad55acd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const isDefaultSelection = | ||
| `${selection.provider}/${selection.model}` === resolvedDefaultRef; | ||
|
|
||
| await updateSessionStore(storePath, (store) => { |
There was a problem hiding this comment.
Validate callback model against current allowlist
Persisting selection.provider/selection.model directly to the session store here skips the allowlist validation that /model applies via the model-selection path (which rejects refs not in allowedModelKeys). This means a stale inline button can still pin a model after policy changes (for example, when agents.defaults.models is tightened or catalog entries are removed), because the callback payload is accepted and written without re-checking current allowed models.
Useful? React with 👍 / 👎.
d66ad55 to
2d5c316
Compare
|
Fixed! Added allowlist validation before persisting the model selection. Changes: const modelSet = byProvider.get(selection.provider);
if (!modelSet?.has(selection.model)) {
await editMessageWithButtons(
`❌ Model "${selection.provider}/${selection.model}" is not allowed.`,
[],
);
return;
}The Updated commit: 2d5c316 |
2d5c316 to
10674f2
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 10674f2cbe
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
The synthetic message approach used for Telegram model button callbacks was silently failing - the callback was answered but the model never actually changed. This replaces the broken synthetic /model command with direct session store updates. Changes: - Replace processMessage() synthetic approach with updateSessionStore() - Add applyModelOverrideToSessionEntry() to directly set model override - Update resolveTelegramSessionState() to return sessionKey - Add visual feedback with ✅/❌ emojis and bold text - Remove inline keyboard after successful selection - Fix isDefault detection: selecting default model now clears override instead of pinning it, matching /model command behavior - Auth checks already applied before callback handler (lines 1108-1157) - Properly resolve agent-specific default model from agents.list or agents.defaults to handle shorthand refs like 'gpt-4o' - Add allowlist validation: verify model is in byProvider before persisting to prevent stale buttons from bypassing policy changes Fixes the bug where clicking model buttons appeared to work but didn't actually change the active model. Test: Click /models → select provider → select model → verify message shows 'Model changed to X' and next message uses that model.
d9eaea1 to
de07585
Compare
|
Merged via squash.
Thanks @avirweb! |
Merged via squash. Prepared head SHA: de07585 Co-authored-by: avirweb <[email protected]> Co-authored-by: velvet-shark <[email protected]> Reviewed-by: @velvet-shark
* main: (120 commits) docs: sync Feishu secretref credential matrix Subagents: stop retrying external completion timeouts (openclaw#41235) (openclaw#43847) Security: require Feishu webhook encrypt key (openclaw#44087) Hardening: normalize Unicode command obfuscation detection (openclaw#44091) Hardening: tighten preauth WebSocket handshake limits (openclaw#44089) Security: preserve Feishu reaction chat type (openclaw#44088) Hardening: require LINE webhook signatures (openclaw#44090) fix(mattermost): pass mediaLocalRoots through reply delivery (openclaw#44021) ci: tighten cache docs and node22 gate ci: restore PR pnpm cache fallback ci: harden pnpm sticky cache on PRs build: align Node 22 guidance with 22.16 minimum build: raise Node 22 compatibility floor to 22.16 build: default to Node 24 and keep Node 22 compat Agents: adapt pi-ai oauth and payload hooks fix(status): resolve context window by provider-qualified key, prefer max on bare-id collision, solve openclaw#35976 (openclaw#36389) fix: carry observed overflow token counts into compaction (openclaw#40357) fix(telegram): allow fallback models in /model validation (openclaw#40105) feat(mattermost): add replyToMode support (off | first | all) (openclaw#29587) docs: add Kubernetes install guide, setup script, and manifests (openclaw#34492) ...
…0105) Merged via squash. Prepared head SHA: de07585 Co-authored-by: avirweb <[email protected]> Co-authored-by: velvet-shark <[email protected]> Reviewed-by: @velvet-shark
…0105) Merged via squash. Prepared head SHA: de07585 Co-authored-by: avirweb <[email protected]> Co-authored-by: velvet-shark <[email protected]> Reviewed-by: @velvet-shark (cherry picked from commit f2e28fc)
PR Description: Fix Telegram Model Button Selection
Problem Statement
Telegram inline button model selection was broken in two ways:
Issue 1: Model buttons don't actually change the model (CRITICAL BUG)
When clicking a model button in Telegram (e.g., "gpt-5.3-codex"), the button click was registered but the model never actually changed. The synthetic message approach (
/model provider/modelviaprocessMessage()) silently failed - the callback query was answered, no error was shown, but the model remained unchanged.Root Cause: The synthetic message processing pipeline doesn't properly trigger model overrides in the session store. The code created a fake
/modelmessage and passed it throughprocessMessage(), but this never actually updated the session's model override fields.Issue 2: Fallback models not shown in model picker
Fallback models configured in
agents.defaults.model.fallbackswere not included in the/modelscommand output, making them inaccessible via the Telegram UI.Solution
Fix 1: Direct Session Store Model Override (bot-handlers.ts)
Replaced the broken synthetic message approach with direct session store updates:
Key Changes:
/modelcommand withupdateSessionStore()+applyModelOverrideToSessionEntry()/modelcommand behaviorCode:
Fix 2: Include Fallback Models in Allowed Set (model-selection.ts)
Added fallback models to the allowed model set so they appear in the
/modelscommand.Files Changed
src/telegram/bot-handlers.ts- Fixed model button click with proper auth and default handlingsrc/agents/model-selection.ts- Added fallback models to allowed setsrc/agents/model-selection.test.ts- Added tests for fallback model inclusionTesting
Test Scenario 1: Model Button Click
/modelsin TelegramTest Scenario 2: Select Default Model
Test Scenario 3: After /reset
/resetto clear session/modelsSecurity Notes
inlineButtonsScopeandauthorizeTelegramEventSenderchecks (lines 1108-1157)Related Issues
Checklist