Skip to content

feat(components): follow per-model Cursor config options in composer selectors - #345

Open
Tigatron wants to merge 5 commits into
LodyAI:mainfrom
Tigatron:feat/cursor-acp-composer-per-model-options
Open

feat(components): follow per-model Cursor config options in composer selectors#345
Tigatron wants to merge 5 commits into
LodyAI:mainfrom
Tigatron:feat/cursor-acp-composer-per-model-options

Conversation

@Tigatron

@Tigatron Tigatron commented Sep 3, 2026

Copy link
Copy Markdown

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 thinking select occupied the Reasoning row and hid the effort ladder; and a fast select whose values are true/false fell 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

  • Stacked on feat(cli): cache Cursor per-model config options from capability probes #344 (draft until it merges): the first three commits de9b74a, be8f415, 2675f4f are that PR's foundation and are included so this branch typechecks and runs CI on its own; review the two commits after 2675f4f.
  • Landing, draft, session, and Role selector catalogs resolve through resolveAcpConfigOptionsForModel with the model read from the channel the composer writes: the model config option for registry/custom agents, the dedicated picker for builtin, and no currentValue fallback (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 (Cursor thinking true/false, Kimi thinking off/on) form a new thoughtToggleSelectors bucket and render as a Thinking toggle row between Plan and Fast in the desktop run-config menu and the mobile sheet, reusing the existing ToggleItem/ToggleRow with 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.
  • Surfaces without a toggle row keep rendering these options: the bottom bar renders them through its existing thought-level path, the Tasks menu binds the Reasoning row to the ladder when there is one and lists other toggles as provider-defined selects, and Recently used labels fall back to the toggle when no ladder exists.
  • Fast accepts boolean, on/off, and true/false selects and writes back the advertised representation.
  • Stories gain a registry Cursor catalog case per surface; no locale key added (labels come from the agent's option names).

Before / after

Before After
Every model showed the probe-time model's options. The selected model's own options; models without options show none.
Cursor thinking took the Reasoning row and hid effort. Thinking is its own toggle row; Reasoning shows the effort ladder.
fast with true/false values rendered as a generic dropdown. Fast toggle with the advertised value written back.

Test plan

  • cd packages/components && corepack pnpm exec tsgo --noEmit && corepack pnpm exec vitest run — 423 files / 3053 tests passed, including new coverage in tests/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, and tests/agent-role-detail-pane.test.tsx (pinned options resolved against the Role's model; fails without the pass-through).
  • Root corepack pnpm typecheck, corepack pnpm lint (0 errors), corepack pnpm format:check, corepack pnpm lint:i18n — passed.
  • Desktop app end to end with an isolated LODY_DATA_DIR: claude-opus-4-7 shows 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-pro shows no per-model rows; gpt-5.4 shows 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

  • Review focus: resolveComposerTargetModelId/resolveCatalogForModel in packages/components/src/components/shared/acp-selector-options.ts (which channel names the model, and the deliberate absence of a currentValue fallback), the thoughtToggleSelectors split in packages/components/src/lib/acp-selector-order.ts, and the stored-value pass-through in agent-role-editor-dialog.tsx (fed from editor.value, never from the derived defaults).
  • Decisions to challenge: rendering Kimi's off/on thinking as 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).
  • Plausible failures / evidence gaps: a live session whose runtime baseline still carries a pre-opt-in variant model id composes the snapshot (by design) until the user re-picks; the Tasks menu and Recently used surfaces were verified by unit tests only; mobile was not exercised on a device.

Authoring context

  • User goal / directives: Show exactly the selected Cursor model's options in every composer surface, including existing sessions, with a Thinking toggle row and face mark, while keeping the change traceable to those surfaces.
  • Constraints / non-goals: No SessionDoc change; the composer stays controlled and reads capabilities through the existing selector context; no new i18n strings; the bottom bar gets no new component; Role editor/detail pane receive only a stored-value pass-through.
  • Risk-bearing decisions: The target model is read from the composer's write channel because a live session seeds the picker candidate from a stale runtime baseline; the bucket split changes how any agent's toggle-shaped thought_level option renders (today only Cursor and Kimi publish one).
  • Destructive or irreversible behavior: None; UI-only derivation with no persistence changes.
  • Deliberately not done or tested: A dedicated Thinking row in the Tasks run-config menu; on-device mobile verification; analytics bucketing left unchanged.
  • Unknowns / confidence: High confidence in the composition and bucket behavior (unit, story, and desktop verification); medium on visual polish of the toggle rows across themes, reviewed via stories only.

Tigatron and others added 5 commits September 3, 2026 16:42
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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T11:08:10.624182Z 17aa0a7 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 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[]>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant