openclaw - ✅(Solved) Fix [Bug]: inline /model <provider/model> <message> is stripped but not applied as a one-shot model override [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#44618Fetched 2026-04-08 00:44:29
View on GitHub
Comments
1
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
commented ×1cross-referenced ×1

/model <provider/model> <message> inside a normal chat message is currently stripped from the prompt, but the selected model is not applied to that message.

This shows up most obviously on chat surfaces like Telegram and Discourse where users reasonably expect:

  • /model openai/gpt-5.2 hello to answer hello with openai/gpt-5.2
  • without persisting the session override

Instead, OpenClaw strips /model ... and continues the run on the existing/default model.

Root Cause

resolveReplyDirectives() clears inline directives whenever the stripped body still contains normal text:

  • src/auto-reply/reply/get-reply-directives.ts

That means mixed messages like:

/model openai/gpt-4.1-mini please sync now

lose hasModelDirective before model selection is applied.

There is already a directive-only path for persistent /model ..., but there is no parallel one-shot path for mixed-message /model ... <body>.

Fix Action

Fixed

PR fix notes

PR #44621: fix(reply): apply inline /model as one-shot override in mixed messages

Description (problem / solution / changelog)

Summary

Fixes mixed-message /model so:

  • /model <provider/model> <message> applies the selected model to the current run
  • the directive text is still stripped from the prompt body
  • the session override is not persisted for later turns

This closes the gap described in #44618, where inline /model was parsed and stripped but silently fell back to the current/default model.

Closes #44618

What changed

  • Preserve mixed-message /model as one-shot directive state instead of clearing it outright in resolveReplyDirectives().
  • Apply that one-shot model/provider selection in the reply pipeline without writing providerOverride / modelOverride to the session store.
  • Thread one-shot auth-profile selection through run preparation so /model provider/model@profile <body> can stay non-persistent as well.
  • Recompute model defaults after the one-shot switch so model-derived thinking/reasoning defaults follow the effective runtime model.
  • Update slash-command docs to clarify /model <provider/model> <message> semantics in normal chat.
  • Add regression coverage for:
    • directive-only /model still persisting normally
    • mixed-message /model applying only to the current turn
    • follow-up turns returning to the session/default model

Why this approach

The existing directive-only path already handles persistent /model ... correctly. The missing piece was a parallel one-shot path for mixed messages. This PR keeps those semantics separate:

  • directive-only /model ... => persistent session override
  • mixed-message /model ... <body> => one-shot runtime override only

That matches the current docs better and avoids silent model fallback on authorized inline usage.

Validation

Passed:

  • pnpm --dir /tmp/openclaw-inline-model-fix exec vitest run src/auto-reply/reply.directive.directive-behavior.defaults-think-low-reasoning-capable-models-no.test.ts --config vitest.config.ts
  • pnpm --dir /tmp/openclaw-inline-model-fix exec vitest run src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.test.ts --config vitest.config.ts
  • pnpm --dir /tmp/openclaw-inline-model-fix exec vitest run src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts --config vitest.config.ts
  • pnpm --dir /tmp/openclaw-inline-model-fix exec vitest run src/agents/auth-profiles/session-override.test.ts --config vitest.config.ts
  • pnpm --dir /tmp/openclaw-inline-model-fix exec vitest run src/auto-reply/reply/get-reply-run.media-only.test.ts --config vitest.config.ts
  • pnpm --dir /tmp/openclaw-inline-model-fix exec vitest run src/auto-reply/model.test.ts --config vitest.config.ts

Full tsc --noEmit on this checkout is still blocked by unrelated missing optional extension/UI dependencies in the local environment (for example OpenTelemetry, Matrix, Twurple, Vite UI packages).

Related issues

  • Related: #12141
  • Related: #27240
  • Related: #40637

Related PRs

  • Related: #24640
  • Related: #43837

Changed files

  • docs/tools/slash-commands.md (modified, +2/-1)
  • src/agents/auth-profiles/session-override.ts (modified, +5/-0)
  • src/auto-reply/reply.directive.directive-behavior.defaults-think-low-reasoning-capable-models-no.test.ts (modified, +45/-17)
  • src/auto-reply/reply.directive.directive-behavior.e2e-mocks.ts (modified, +1/-1)
  • src/auto-reply/reply/directive-handling.parse.ts (modified, +5/-0)
  • src/auto-reply/reply/get-reply-directives-apply.ts (modified, +40/-1)
  • src/auto-reply/reply/get-reply-directives-utils.ts (modified, +3/-0)
  • src/auto-reply/reply/get-reply-directives.ts (modified, +58/-21)
  • src/auto-reply/reply/get-reply-run.ts (modified, +6/-1)
  • src/auto-reply/reply/get-reply.ts (modified, +2/-0)

Code Example

/model openai/gpt-4.1-mini please sync now

---

/model openai/gpt-4.1-mini please sync now
RAW_BUFFERClick to expand / collapse

Summary

/model <provider/model> <message> inside a normal chat message is currently stripped from the prompt, but the selected model is not applied to that message.

This shows up most obviously on chat surfaces like Telegram and Discourse where users reasonably expect:

  • /model openai/gpt-5.2 hello to answer hello with openai/gpt-5.2
  • without persisting the session override

Instead, OpenClaw strips /model ... and continues the run on the existing/default model.

Why this looks like a bug (not just docs confusion)

The current docs say inline directives in normal chat are treated as "inline hints" and do not persist session settings.

That wording strongly implies per-message behavior for /model, but the current implementation does not do that.

Reproduction

Any authorized sender on a text-command-enabled surface:

/model openai/gpt-4.1-mini please sync now

Current behavior:

  • the agent runs
  • /model openai/gpt-4.1-mini is removed from the prompt
  • the run still uses the session/default model
  • no session override is stored

Expected behavior:

  • the prompt body becomes please sync now
  • that single run uses openai/gpt-4.1-mini
  • the session override remains unchanged for later messages

Root cause

resolveReplyDirectives() clears inline directives whenever the stripped body still contains normal text:

  • src/auto-reply/reply/get-reply-directives.ts

That means mixed messages like:

/model openai/gpt-4.1-mini please sync now

lose hasModelDirective before model selection is applied.

There is already a directive-only path for persistent /model ..., but there is no parallel one-shot path for mixed-message /model ... <body>.

Why this is channel-agnostic core behavior

I reproduced the user-visible symptom on Telegram and Discourse, but the root cause is in core reply/directive handling, not in a channel plugin.

The parser recognizes inline /model tokens correctly; the mixed-message clearing step is what drops them.

Proposed fix

Preserve mixed-message /model as a one-shot directive state, then:

  1. apply the selected provider/model to the current run only
  2. keep the cleaned body without the directive text
  3. do not persist providerOverride / modelOverride to the session store
  4. leave later turns on the existing session/default model

Notes

This issue is intentionally scoped to /model in mixed messages.

There is related ongoing work for one-shot /think <level> <body> semantics, but /model should not keep silently falling back to the default model when docs and user expectation both point to per-message behavior.

Related issues

  • Related: #12141
  • Related: #27240
  • Related: #40637

Related PRs

  • Related: #24640
  • Related: #43837

Tracking PR: to be added after opening.

extent analysis

Fix Plan

To address the issue, we need to modify the resolveReplyDirectives() function to preserve the /model directive in mixed messages and apply it as a one-shot directive. Here are the steps:

  • Modify get-reply-directives.ts to check for mixed messages containing /model directives.
  • If a mixed message is found, extract the /model directive and apply it to the current run.
  • Clean the body by removing the /model directive text.
  • Ensure that the providerOverride and modelOverride are not persisted to the session store.

Example code changes:

// src/auto-reply/reply/get-reply-directives.ts

function resolveReplyDirectives(message: string): { body: string; directives: any } {
  // ... existing code ...

  // Check for mixed messages containing /model directives
  const modelMatch = message.match(/^\/model (.+) (.+)$/);
  if (modelMatch) {
    const modelDirective = modelMatch[1];
    const body = modelMatch[2];

    // Apply the /model directive to the current run
    const selectedModel = getModelFromDirective(modelDirective);
    // ... apply the selected model to the current run ...

    // Clean the body by removing the /model directive text
    const cleanedBody = body;

    // Return the cleaned body and directives
    return { body: cleanedBody, directives: { ...directives, model: selectedModel } };
  }

  // ... existing code ...
}

Verification

To verify that the fix worked, test the following scenarios:

  • Send a mixed message with a /model directive (e.g., /model openai/gpt-4.1-mini please sync now) and verify that the response is generated using the specified model.
  • Verify that the session override remains unchanged for later messages.
  • Test on different channels (e.g., Telegram, Discourse) to ensure that the fix is channel-agnostic.

Extra Tips

  • Ensure that the getModelFromDirective() function is implemented correctly to extract the model from the /model directive.
  • Consider adding logging or debugging statements to verify that the /model directive is being applied correctly.
  • Review related issues and PRs to ensure that this fix does not introduce any regressions or conflicts with ongoing work.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: inline /model <provider/model> <message> is stripped but not applied as a one-shot model override [1 pull requests, 1 comments, 1 participants]