feat(cli): cache Cursor per-model config options from capability probes - #344
Open
Tigatron wants to merge 3 commits into
Open
feat(cli): cache Cursor per-model config options from capability probes#344Tigatron wants to merge 3 commits into
Tigatron wants to merge 3 commits into
Conversation
An ACP probe snapshot only describes the model that was current when the probe ran, while an agent such as cursor-agent publishes a distinct option set for every model. AcpCapabilityCacheEntry gains configOptionsByModel, the per-model catalog an explicit probe may store, where a model mapped to an empty list has no model-dependent options and a missing key means the catalog does not know that model. resolveAcpConfigOptionsForModel is the one composition rule for a model's options: the snapshot options no catalog entry owns plus the selected model's entry, falling back to the snapshot for an unknown model. model and mode options always come from the snapshot, so a catalog can neither shrink the model picker nor replace the permission modes. resolveAcpTargetModelId names the model a run config targets. Fast mode now also recognises a select whose values are exactly true and false, the shape cursor-agent uses for boolean parameters, and writes back the advertised representation instead of on/off. Implemented with cursor-grok-4.6-xhigh-fast subagents. Model: claude-fable-5.1 Co-authored-by: Cursor <cursoragent@cursor.com>
Registry Cursor now declares clientCapabilities._meta.parameterizedModelPicker at initialize, so probes and sessions see clean model ids plus per-model thinking, effort, context, and fast options instead of exploded variant strings whose in-session model switch silently fails. The gate is registry identity, never a same-named custom or builtin config. A session/new snapshot only describes the model current at probe time, so an explicit machine/acp-capabilities-refresh additionally calls the agent's cursor/list_available_models once and stores every model's options as AcpCapabilityCacheEntry.configOptionsByModel. That method is the only extension cursor-agent serves and performs no writes, unlike enumerating models through session/set_config_option, which rewrites the user's global Cursor config. Real sessions never fetch the catalog; their snapshot write keeps the stored catalog for the same sourceVersion, and the unchanged-entry comparison includes it so a refreshed catalog is committed. JSON-RPC -32601 means no catalog; a response that fails validation or lists a model twice, a timeout, or any other failure fails the probe with [ACP_CAPABILITIES_INCOMPLETE] so the settings test button can retry. Implemented with cursor-grok-4.6-xhigh-fast subagents. Model: claude-fable-5.1 Co-authored-by: Cursor <cursoragent@cursor.com>
Registry and custom agents carry the selected model in the model config option rather than modelId, and the applier switched it inside the option loop at its key position. Cursor validates thinking, effort, and fast against the model that is current when each option arrives, so options ordered before the model key were checked against the previous model and rejected. Apply the config-option model right after the explicit modelId path and skip its loop entry; the unstable_setSessionModel channel and its fallback are unchanged. Implemented with cursor-grok-4.6-xhigh-fast subagents. Model: claude-fable-5.1 Co-authored-by: Cursor <cursoragent@cursor.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This was referenced Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Refs #343
Problem / pressure
Lody's registry Cursor agent runs in cursor-agent's legacy "variants" mode because the ACP client never declares
clientCapabilities._meta.parameterizedModelPicker: model ids arrive exploded, no per-model options are published, and an in-session model switch throughsession/set_config_optionsilently fails to match. Once the picker is enabled the second problem surfaces: asession/newsnapshot describes only the model current at probe time, while Cursor rebuilds thinking/effort/context/fast per model and rejects values the new model lacks, so nothing outside a live session can know another model's options. The run-config applier also sent per-model options before the model when the model lived inconfigOptionValues, which Cursor validates against the previous model.Summary
cliType: 'registry'andagentType: 'cursor') declaresparameterizedModelPickerat initialize;AgentClientgains a genericrequestExtMethodwith abort support.machine/acp-capabilities-refreshprobe calls Cursor's side-effect-freecursor/list_available_modelsonce aftersession/newand stores every model's non-model/mode options asAcpCapabilityCacheEntry.configOptionsByModel([]= a known model with no options; missing key = unknown model). JSON-RPC-32601means no catalog; a response that fails Zod validation or lists a model twice, a timeout, an abort, or any other failure fails the probe with[ACP_CAPABILITIES_INCOMPLETE]so the Settings Test action can retry. Real sessions never fetch it; their snapshot write preserves the stored catalog for the samesourceVersion, and the unchanged-entry comparison includes the field.@lody/sharedgains the one composition ruleresolveAcpConfigOptionsForModel(snapshot options no catalog entry owns + the selected model's entry;model/modealways from the snapshot; unknown model → snapshot),resolveAcpTargetModelId, and true/false toggle predicates; Fast mode now recognises a select whose values are exactlytrue/false.unstable_setSessionModelchannel and its fallback are unchanged.ACP_CAPABILITY_CACHE_VERSIONchange; consumers of the catalog (composer, MCP mapping) follow in separate PRs.Before / after
parameterizedModelPicker: exploded model ids, no per-model options, silent model-switch failures.session/set_config_optionmodel switches take effect.configOptionsByModelfrom an explicit probe, preserved across same-version session writes.Test plan
cd packages/shared && corepack pnpm exec tsgo --noEmit && corepack pnpm exec vitest run tests/acp-run-config.test.ts— 29 passed (composition rule, target-model resolution, toggle predicates).cd apps/cli && corepack pnpm exec tsgo --noEmit && corepack pnpm exec vitest run src/agent/cursor-acp.test.ts src/agent/acp-capabilities.test.ts src/agent/agent-client-initialize.test.ts src/lib/loro/machine-document-capabilities.test.ts src/session/acp-session-config-applier.test.ts tests/message-handler-fable-fast-mode.test.ts tests/session-execution-service.test.ts— all passed (catalog parsing,-32601, INCOMPLETE paths with fake timers, identity-gated initialize meta, same-version catalog preservation and change detection, applier ordering).corepack pnpm typecheck,corepack pnpm lint(0 errors),corepack pnpm format:check,corepack pnpm lint:i18n,check:code-collab-imports,check:platform-boundaries,check:public-boundary— passed.corepack pnpm test:ci— all packages pass except two pre-existingapps/cliworktree tests (session-manager.test.ts"rebuilds a prepared worktree whose directory disappeared before adoption",worktree-manager.create.test.ts"should rebuild a missing registered worktree") that fail identically on a cleanmainwith git 2.51.0; unrelated to this change.cursor-agent 2026.08.31protocol probe (no prompt sent):cursor/list_available_modelsreturned 37 unique models whose option shapes match the parser (allselect,thinking/fastvaluesfalse/true,contextcategorymodel_config, some models with no options); an unknown ext method returned-32601. The captured payload parses throughfetchCursorModelCataloginto 37 entries.LODY_DATA_DIR: add registry Cursor, refresh → Ready; a turn with Thinking on and effort high completes without a rejected-selection notice; the same session switched togpt-5.4runs a second turn cleanly. Skipped: Windows/Linux runs; a real-32601agent (covered by unit tests only).Context handoff
Instructions for reviewing agents
apps/cli/src/agent/cursor-acp.ts(validation and error mapping of the catalog response),MachineDocument.updateAcpCapabilitiesinapps/cli/src/lib/loro/doc.ts(same-sourceVersionpreservation and the change detector including the new field), andresolveAcpConfigOptionsForModelinpackages/shared/src/acp-run-config.ts(union rule and themodel/modeexclusion).ACP_CAPABILITY_CACHE_VERSIONunchanged because the field is additive.agent-client.ts↔cursor-acp.tsimport cycle is function-scoped only; the applier still logs (not warns) when a config-option model switch is rejected, unchanged from before.Authoring context
modelReasoningEffortsfor Grok) is sibling work this composes with.parameterizedModelPickerchanges Cursor's advertised model ids for new probes and sessions; the catalog is stored only from explicit probes and preserved across same-version session writes; enumeration throughsession/set_config_optionis deliberately forbidden because it rewrites the user's global Cursor config.