Skip to content

fix(server): write thinking with the switched-to Cursor model's option id - #6995

Open
hungthai1401 wants to merge 2 commits into
getpaseo:mainfrom
hungthai1401:fix/cursor-model-thinking-option-id
Open

hungthai1401 wants to merge 2 commits into
getpaseo:mainfrom
hungthai1401:fix/cursor-model-thinking-option-id

Conversation

@hungthai1401

Copy link
Copy Markdown

Linked issue

No issue filed. The report and reproduction are below. Same root cause as #5274 (stale option snapshot after a Cursor model switch, there for Fast): this PR fixes it for thinking.

Type of change

  • Bug fix
  • New feature
  • Enhancement
  • Refactor
  • Docs

Reasoning

Create a Cursor agent on Grok 4.6 with a thinking level stored on it (or resume one) while the Cursor CLI's saved default model is GPT-5.6 Sol. The agent is not created, and the composer shows Invalid params: Unknown model config option: reasoning. The mirror case (default Grok, agent on GPT) fails with ...option: effort. It only happens when the stored thinking level differs from the session's current value, so it shows up for some agents and not others.

Cursor names its thinking option per model. From cursor/list_available_models on cursor-agent 2026.10.14:

thought_level id models
effort grok-4.6, grok-4.5, claude-opus-5-5, gemini-3.7-flash, ... (6)
reasoning_effort grok-4.7, claude-sonnet-5-5, gemini-4-argon, gemini-3.8-flash
reasoning gpt-5.x, kimi-k3, glm-5.x (14)
thinking the other Claude models (13)

setModelWithSelection switches models with unstable_setSessionModel. Cursor answers it with {}, so Paseo keeps the previous model's configOptions. applyConfiguredOverrides then calls setThinkingOption, which finds the old model's option in that list and writes it for the new model. Cursor rejects the unknown id, the error escapes initializeNewSession / initializeResumedSession, and the agent never starts.

Cursor answers a config-option model write (setSessionConfigOption with the model option) with the new model's configOptions, and applyConfigOptionResponse already stores them. The Cursor client now uses that path when the session exposes a model option, so the thinking and Fast writes that follow use the right ids.

Goals

  • Creating or resuming a Cursor agent on a model whose thinking option id differs from the session's starting model works, and the stored thinking level is applied (not dropped).
  • The Fast write that follows a model switch also sees the new model's options.
  • Other ACP providers are unchanged. The behavior is off by default and only the Cursor client turns it on (switchModelViaConfigOption).
  • A regression test fails on the old code with the reported error.

Non-goals

QA

Environment: macOS 15, cursor-agent 2026.10.14-941492e, logged in. Server change only, no UI.

Reproduced the cause against the real CLI over ACP. Session starts on grok-4.6; after switching:

unstable_setSessionModel gpt-5.6-sol -> {}                    # no options returned
unstable_setSessionModel grok-4.6    -> {}
setSessionConfigOption reasoning=medium (on grok) -> Invalid params
setSessionConfigOption effort=medium    (on grok) -> ok, ["mode","model","effort","fast"]

setSessionConfigOption model=gpt-5.6-sol -> ["mode=agent","model=gpt-5.6-sol","context=272k","reasoning=high","fast=false"]
setSessionConfigOption model=grok-4.6    -> ["mode=agent","model=grok-4.6","effort=medium","fast=false"]

Before and after through createSession with the real CLI (a throwaway script that built the Cursor client with and without the flag). The saved default was gpt-5.6-sol, the agent asks for grok-4.6 with thinking low:

before grok-4.6/low: FAIL Invalid params: Unknown model config option: reasoning
after  grok-4.6/low: OK model=grok-4.6 thinking=low

The regression test (writes the stored thinking level with the id of the model it switched to) fails on the old acp-agent.ts with the same error and passes with the change:

FAIL cursor-acp-agent.test.ts > CursorACPAgentClient session start > writes the stored thinking level with the id of the model it switched to
ACPRequestError: Invalid params: Unknown model config option: reasoning

After, in a checkout installed with npm ci and npm run build:server:

  • vitest run on cursor-acp-agent, cursor-acp-catalog, acp-agent and generic-acp-agent: 4 files, 137 tests passed.
  • npm run typecheck, npm run lint and npm run format:check pass (the pre-commit hook runs them too).

Not covered: I ran createSession, not resumeSession through a live daemon, though both call the same applyConfiguredOverrides. Only macOS and the CLI were exercised. Nothing in the change is platform specific, but I did not run it on Windows or Linux.

Checklist

  • Plugin changes follow the SDK import boundaries (if applicable)
  • One focused change
  • npm run typecheck passes
  • npm run lint passes
  • npm run format passes
  • QA evidence
  • Tests added or updated where it made sense

…n id

Cursor names its thinking option per model: gpt uses `reasoning`, grok-4.6
uses `effort`, grok-4.7 uses `reasoning_effort`, Claude uses `thinking`.
Paseo switched models with session/set_model, which answers with an empty
result, so it kept the previous model's config options. The stored thinking
level was then written with the old model's option id, and Cursor rejected
it with "Unknown model config option". Creating or resuming an agent failed
whenever the CLI's default model used a different thinking id than the
agent's model.

Cursor now switches models with a config-option write, which returns the new
model's options, so the thinking and Fast writes that follow use the right
ids. The behavior sits behind `switchModelViaConfigOption`, set only by the
Cursor client, so the other ACP providers keep session/set_model.

Checked against a real cursor-agent: creating grok-4.6 with thinking `low`
from a gpt default failed before the change and starts after it. The new
test fails on the old code with the same error. To undo, revert this commit.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@greptile-apps

greptile-apps Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge.

Summary

Cursor now switches models through setSessionConfigOption when the session offers a matching model choice.

  • Cursor applies saved thinking with the selected model’s option name.

No actionable issues found.

Diagram

sequenceDiagram
  participant Session as Cursor session
  participant CLI as Cursor CLI
  Session->>CLI: setSessionConfigOption(model)
  CLI-->>Session: New model configOptions
  Session->>Session: Store refreshed options
  Session->>CLI: Write thinking with new option ID
  Session->>CLI: Apply stored Fast setting
Loading

Reviews (2) · Last reviewed commit: "Merge branch 'main' into fix/cursor-mode..." · Reviewed by Greptile

@paseo-bot

paseo-bot Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

This is tracked as #7148. Work on Paseo is organized by issue rather than by pull request: #7148 is picked up when it is reached, and a fix that builds on yours credits you as a co-author. Updates go on #7148.

Thanks for the pull request.

@paseo-bot paseo-bot Bot added bug Reproducible unintended behavior triaged Reviewed by maintainer triage diagnosing The diagnose bot is working on this issue labels Oct 16, 2026

@paseo-bot paseo-bot 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.

Verified the startup fix for #7148 against the real Cursor CLI on Linux. With thinking low, switching from a GPT-5.6 Sol default to Grok 4.6 fails on main with Unknown model config option: reasoning; the reverse fails with ...option: effort. Both start with the requested model and thinking on 39b9de5c3.

The regression test fails on main with the matching error, and all 137 tests across the four related provider suites pass on this head. Local server build, repository typecheck and lint, and formatting checks for the changed files pass.

Resume remains unverified: the attempted conversation did not produce a resumable session, and lookup failed before thinking could be applied. The GUI was not exercised; this verifies provider startup.

CI run 38835707322 is green on this head, including Linux and Windows server tests. No review threads are unresolved. This PR is the verified fix for the reported startup failure; updates remain here.

@paseo-bot paseo-bot Bot added diagnosed The diagnose bot investigated this; on a PR, the bot authored it and removed diagnosing The diagnose bot is working on this issue labels Oct 16, 2026

@paseo-bot paseo-bot 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.

Stopped for the maintainer. The fix is correct at the layer that owns the stale state, and every checklist item passed on a real Cursor run, but a reply from a named Cursor model (Grok 4.6 or GPT-5.6 Sol) after the switch could not be run here, so that part of the checklist is not a pass.

Product delta: a Cursor agent created, resumed, or switched to a model whose thinking option has a different id than the model the Cursor CLI started on now starts with the requested model and thinking level, where it used to fail with Invalid params: Unknown model config option: reasoning (or effort).
Protocol delta: none.

For the maintainer:

  • Merge without having seen a named Cursor model answer a turn after the new model switch? Session setup, resume, and the switch were observed working against the real Cursor CLI. yes or no (yes)

  • Durability: adds one internal boolean on the ACP client options (switchModelViaConfigOption), not a user setting or wire change.

  • Specificity: one contained branch in setModelWithSelection; only Cursor opts in, other ACP providers take the unchanged path.

  • Risk: ships beside everything else; for Cursor the model is now switched with session/set_config_option instead of session/set_model, which Cursor answers with the new model's options.

  • Serves: any Cursor user whose agent's model differs from the CLI's starting model and whose stored thinking level differs; no workaround besides clearing thinking, and the fix is about 17 lines plus a test.

  • Recommendation: merge.

What the pull request does

For Cursor, ACPAgentSession.setModelWithSelection (packages/server/src/server/agent/providers/acp-agent.ts) switches the model through the model config option when the session advertises it, so the session holds the new model's config options and the thinking and Fast writes that follow use the new model's option ids.

Review

  1. Fixes what was reported: yes. On main, unstable_setSessionModel succeeds but Cursor answers {}, so configOptions keeps the previous model's thought_level option, and setThinkingOption in applyConfiguredOverrides writes reasoning to a Grok session. The error escapes initializeNewSession / initializeResumedSession. The regression test drives initializeNewSession through a scripted ACP transport and fails on the merge base with the reported error.
  2. Right fix: yes. The stale state is ACPAgentSession.configOptions, and the change makes the session take the write whose response carries the new options, through the existing applyConfigOptionResponse. The set_model preference dates from a large unrelated commit, not a decision about this behavior. A wider reshaping is possible: preferring the config-option write for every ACP provider that advertises a model option would remove the flag and the stale-options case behind isStaleFeatureValueError (#5274) for all of them. That is not needed for this fix.
  3. Anything else: no protocol, schema, user setting, dependency, or design change. Other providers are unchanged by construction: the flag defaults to false, and only CursorACPAgentClient sets it.
  4. Worth shipping: yes, a small change for a startup failure any Cursor user can hit by picking a different model with a thinking level.

QA checklist

All runs on the real Cursor CLI, an isolated daemon, and the web app from this branch, with the merge base swapped in for comparison.

  1. Reported scenario (CLI on GPT-5.6 Sol, agent created on Grok 4.6 / Low): pass for setup. Head creates the agent showing Grok 4.6 / Low; the merge base fails with Unknown model config option: reasoning. Mirror case (CLI on Grok, agent on GPT-5.6 Sol / Low) starts on head. A reply from the named model was not run.
  2. Regression test: pass. cursor-acp-agent.test.ts 10/10 on head, 1/10 failing on the merge base with the reported error; the four related provider suites 137/137 on head.
  3. Resume: pass for setup. Seeded with a completed turn, switched to Grok 4.6 / Low, CLI default back to GPT, daemon restarted: head resumes showing Grok 4.6 / Low with no error; the merge base fails the same resume with Unknown model config option: reasoning from initializeResumedSession. The follow-up turn on the named model did not produce a reply.
  4. Model switch on a running agent: pass for the switch. Head switches GPT-5.6 Sol to Grok 4.6, shows Grok's thinking choices, and accepts the new level; on the merge base the model changes but setting Low fails with the same error. A reply from the named model was not run.
  5. Fast after a switch at creation: pass. Head creates Grok 4.6 / Low with Fast, and the agent's stored state records Fast on.
  6. Cursor CLI saved default model: no difference from main. Creating or switching an agent updates the CLI's saved default model on both head and the merge base.

Findings outside the checklist

  • A npm run typecheck after rebuilding locally reported two errors in app files the pull request does not touch; an earlier local typecheck on the same head passed and CI typecheck is green on it. Not counted.
  • No review threads.

Decision

Stopped with needs-maintainer. Blocker: no turn on a named Cursor model was observed after the switch through setSessionConfigOption (acp-agent.ts, setModelWithSelection). What settles it: one Cursor turn on Grok 4.6 / Low after starting from a GPT default, answering on Grok 4.6.

Reviewed commit: 39b9de5

@paseo-bot paseo-bot Bot added the needs-maintainer A bot could not resolve this; the maintainer's queue label Oct 16, 2026

@janacm janacm 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.

Looks good. I hit this from a running agent and landed on the same fix independently.

Field report: Paseo 0.11.4 desktop daemon, cursor-agent 2026.10.01-e373342, macOS. On a live Cursor agent, switching GPT-5.4 mini to Claude Opus 5.5 succeeds (set_agent_model_request success), then set_agent_thinking_request (xhigh) fails with Invalid params: Unknown model config option: reasoning. The same daemon log has the startup variant from metadata generation: cursor (gpt-5.4-mini): failed (Invalid params: Unknown model config option: reasoning_effort) and ...: effort. So this also stops branch-name generation from failing over when Cursor is the structured-generation provider.

Against the real CLI (same version): session/set_model answers {} with no config_option_update. session/set_config_option with model=claude-opus-5-5 returns mode, model, context, effort, fast, and the effort write after it succeeds. That matches the QA above.

Two small suggestions, neither blocking:

  • The regression test covers initializeNewSession. The path I hit is setModel followed by setThinkingOption on a live session. It goes through the same setModelWithSelection branch, but a test for it would pin the mid-chat case.
  • The body says no issue was filed. #7148 exists now, so Fixes #7148 would close it on merge.

@janacm

janacm commented Oct 18, 2026

Copy link
Copy Markdown

Ran the open item from the maintainer stop: a Cursor turn on Grok 4.6 / Low after starting from a GPT default, on head 39b9de5c3.

Setup: cursor-agent 2026.10.01-e373342, macOS, CursorACPAgentClient from this branch driven directly (no daemon or UI). I recorded the ACP calls Paseo made.

Running agent: created on GPT-5.6 Sol, then setModel("grok-4.6") and setThinkingOption("low"):

set_config_option model=gpt-5.6-sol -> ok
set_config_option model=grok-4.6 -> ok
set_config_option effort=low -> ok
runtime: model=grok-4.6 thinking=low

Reported scenario: CLI default GPT-5.6 Sol, agent created on Grok 4.6 / Low, then one turn:

set_config_option model=grok-4.6 -> ok
set_config_option effort=low -> ok
prompt: which company trained you, and what is your model name?
reply:  "SpaceXAI and Cursor jointly trained me; my model name is Cursor Grok 4.6."
runtime after turn: model=grok-4.6 thinking=low

No session/set_model call in either flow. The turn answered on Grok 4.6, so the switch through session/set_config_option changes the model that answers, not only the session's options.

Not covered: resume, and the app UI.

@iqshen

iqshen commented Oct 22, 2026

Copy link
Copy Markdown

Independent Linux reproduction confirms the same root cause for the thinking -> reasoning_effort variant in #7148: Paseo 0.12.0 with cursor-agent 2026.10.19-6bbf2c4, CLI default claude-haiku-5-5 using thinking, agent configured as muse-wonder-1.0 / Medium using reasoning_effort, first session creation fails with Invalid params: Unknown model config option: thinking.

This PR's model-switch-through-session/set_config_option approach fixes the underlying stale-options state and preserves the requested thinking level. Non-blocking suggestion: adding thinking -> reasoning_effort alongside the current reasoning -> effort regression case would pin the exact variant reported in #7148.

@Wu-Jiayang

Copy link
Copy Markdown

Additional affected-version confirmation: Paseo daemon 0.15.0, cursor-agent 2026.10.22-27dcf5f, Linux, built-in Cursor provider. Creating a chat on Grok 4.6 / High fails with Invalid params: Unknown model config option: reasoning. A read-only catalog probe confirms this model exposes effort, not reasoning. Host-side evidence is also posted at #4034 (comment); this matches #7148 and this PR's startup scenario.

I reviewed head 39b9de5c3a5664c4d521411b3f7a3c633e43966d. Preferring the model config-option write is the right fix for the stale config-id error, and keeping it Cursor-only limits the change's scope. I did not install this branch or run its full tests; the existing CI/review evidence is separate from my checks.

One adjacent, apparently pre-existing correctness gap remains worth a regression test (not claiming this PR introduced it): the refreshed options do not update the cached thinkingOptionId.

  • applyConfigOptionResponse replaces configOptions, but setModelWithSelection only assigns its return value to currentModel.
  • applyConfiguredOverrides compares the requested thinking level with this.thinkingOptionId, which can still describe the previous model.
  • If the old model's level equals the requested level, but the target model's returned level differs, the thinking write is skipped. Runtime information can then report the requested level while the target model retains a different level.

I isolated this using the installed ACP session implementation with this PR's config-option preference branch applied in memory, a scripted connection, and no live Cursor calls:

old GPT reasoning=high, requested Grok/high
model write returns Grok effort=low
writes: model=grok only
actual scripted target level: low
getRuntimeInfo().thinkingOptionId: high

control: old GPT reasoning=medium, requested Grok/high
writes: model=grok, effort=high
actual and reported level: high

This is an isolated check of the branch plus surrounding logic, not an end-to-end test of the PR checkout. The same missing synchronization is visible in the reviewed head.

Suggestion: reconcile the cached thinking level with the target model's returned thought_level before deciding whether the configured override is already satisfied, and add the equal-old/requested-but-different-target case alongside the current regression. A live setModel test could also assert that runtime thinking reflects the new model's returned value before an explicit thinking write.

The exact reasoning -> effort startup failure is still worth fixing independently of that adjacent state-synchronization gap.

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

bug Reproducible unintended behavior diagnosed The diagnose bot investigated this; on a PR, the bot authored it triaged Reviewed by maintainer triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants