feat(components): follow per-model Cursor config options in composer selectors - #345
feat(components): follow per-model Cursor config options in composer selectors#345Tigatron wants to merge 5 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>
The composer built its selectors from the probe snapshot alone, so a Cursor session showed the probe-time model's thinking, effort, context, and fast options whichever model was selected, and a fast select whose values are true/false fell through as a generic dropdown. Selector catalogs now resolve through resolveAcpConfigOptionsForModel with the model read from the channel the composer writes: the model config option for registry and custom agents, the dedicated picker for builtin ones, and no currentValue fallback, since an unselected model composes the snapshot. The Role editor and detail pane feed the STORED value into that channel so a Role's pinned options resolve against its own model's catalog. Toggle-shaped thought_level options are their own bucket, thoughtToggleSelectors, so a two-valued thinking switch no longer occupies the Reasoning row and hides the effort ladder; the bottom bar, the Tasks menu, and Recently used keep rendering them where no toggle row exists. Fast accepts boolean, on/off, and true/false selects and writes back the advertised value. Implemented with cursor-grok-4.6-xhigh-fast subagents. Model: claude-fable-5.1 Co-authored-by: Cursor <cursoragent@cursor.com>
Cursor publishes thinking as a two-valued select next to a multi-level effort ladder, and Kimi models without effort levels publish thinking as off/on. Both now render as a Thinking toggle row between Plan and Fast in the desktop run-config menu and the mobile sheet, reusing the existing ToggleItem and ToggleRow and the agent's own option label, and both faces carry a Brain mark while the toggle is on, like the Fast mark. The Role inert face follows the same face parts. Stories gain a registry Cursor catalog case per surface. 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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17aa0a7823
ℹ️ 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".
|
|
||
| export type FetchedAcpCapabilities = AcpCapabilitiesResult & { | ||
| capabilitySourceVersion?: string; | ||
| configOptionsByModel?: Record<string, AcpConfigOptionSummary[]>; |
There was a problem hiding this comment.
Invalidate existing caches before relying on the model catalog
When an existing registry Cursor configuration upgrades, its persisted capability entry still has the current ACP_CAPABILITY_CACHE_VERSION (6) and the same registry sourceVersion, so the selector accepts that entry even though it lacks this newly added field. Because configOptionsByModel is populated only by an explicit capability refresh and ordinary sessions never fetch it, existing users remain stuck with the probe-time snapshot behavior until they manually test/refresh the provider. Treat Cursor entries without the catalog as stale or bump the capability-cache version when adding this probe-derived field.
AGENTS.md reference: apps/cli/src/agent/AGENTS.md:L243-L245
Useful? React with 👍 / 👎.
Related issue
Refs #343
Problem / pressure
With the per-model catalog stored by the capability probe, the composer still built its selectors from the probe snapshot alone: a Cursor session showed the probe-time model's thinking, effort, context, and fast options whichever model was selected; a two-valued
thinkingselect occupied the Reasoning row and hid the effort ladder; and afastselect whose values aretrue/falsefell through as a generic dropdown. Landing and draft chats, which have no live ACP session, had no way to show another model's options at all.Summary
de9b74a,be8f415,2675f4fare that PR's foundation and are included so this branch typechecks and runs CI on its own; review the two commits after2675f4f.resolveAcpConfigOptionsForModelwith the model read from the channel the composer writes: themodelconfig option for registry/custom agents, the dedicated picker for builtin, and nocurrentValuefallback (an unselected model composes the snapshot). The Role editor and detail pane feed the STORED value into that channel so a Role's pinned options resolve against its own model's catalog.thought_leveloptions (Cursorthinkingtrue/false, Kimithinkingoff/on) form a newthoughtToggleSelectorsbucket and render as a Thinking toggle row between Plan and Fast in the desktop run-config menu and the mobile sheet, reusing the existingToggleItem/ToggleRowwith the agent's own label; both faces show a Brain mark while on, like the Fast mark. The Reasoning row binds only to multi-level selectors.Before / after
thinkingtook the Reasoning row and hideffort.fastwith true/false values rendered as a generic dropdown.Test plan
cd packages/components && corepack pnpm exec tsgo --noEmit && corepack pnpm exec vitest run— 423 files / 3053 tests passed, including new coverage intests/acp-selector-options.test.ts(per-model composition and the model channel),tests/acp-inline-selector-group.test.ts(bucket routing for Cursor/Kimi/boolean thought options),tests/desktop-run-config-thinking-toggle.test.tsx,tests/run-config-thinking-face.test.tsx, andtests/agent-role-detail-pane.test.tsx(pinned options resolved against the Role's model; fails without the pass-through).corepack pnpm typecheck,corepack pnpm lint(0 errors),corepack pnpm format:check,corepack pnpm lint:i18n— passed.LODY_DATA_DIR:claude-opus-4-7shows Context 300K/1M, effort low…max, Thinking and Fast toggles; toggling Thinking keeps the menu open and the face shows Brain plus the Fast mark;gemini-3.1-proshows no per-model rows;gpt-5.4shows reasoning none…extra-high, Fast, Context and no Thinking; switching the model inside an existing session updates the menu immediately. Skipped: mobile device run (the mobile sheet and face are covered by jsdom tests and stories only).Context handoff
Instructions for reviewing agents
resolveComposerTargetModelId/resolveCatalogForModelinpackages/components/src/components/shared/acp-selector-options.ts(which channel names the model, and the deliberate absence of acurrentValuefallback), thethoughtToggleSelectorssplit inpackages/components/src/lib/acp-selector-order.ts, and the stored-value pass-through inagent-role-editor-dialog.tsx(fed fromeditor.value, never from the derived defaults).thinkingas a toggle row instead of the previous two-row Reasoning dropdown; keeping the Tasks menu without a dedicated Thinking row (toggles surface there as provider-defined selects).Authoring context
thought_leveloption renders (today only Cursor and Kimi publish one).analyticsbucketing left unchanged.