Skip to content

feat(shared): map MCP run config and CLI validation onto the target model's catalog - #346

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

feat(shared): map MCP run config and CLI validation onto the target model's catalog#346
Tigatron wants to merge 5 commits into
LodyAI:mainfrom
Tigatron:feat/cursor-acp-mcp-per-model-mapping

Conversation

@Tigatron

@Tigatron Tigatron commented Sep 3, 2026

Copy link
Copy Markdown

Related issue

Closes #343

Problem / pressure

lody_session_create takes run config semantically, and the mapping onto an agent's option ids read only the probe snapshot: for a Cursor target it could write a reasoning level into the two-valued thinking select, reject fastMode for a model whose own catalog offers it, and lody_session_create_options published the probed model's effort values for every model. CLI and MCP create/chat validated configOptionValues against the same snapshot, so a value the target model allows could be rejected as unknown, a value it lacks could pass, and inherited create defaults kept a parent's opus-only fast/xhigh for a child that will run sonnet.

Summary

  • Stacked on feat(cli): cache Cursor per-model config options from capability probes #344 (draft until it merges): the first three commits 0df6598, 52b6b2e, 3f62760 are that PR's foundation and are included so this branch typechecks and runs CI on its own; review the two commits after 3f62760. Independent of the composer PR.
  • resolveAgentRunConfigSelection reads the target model's composed options: reasoningEffort binds to a multi-level thought_level select when the model has one and to a lone thinking toggle otherwise (Kimi models without effort levels keep working), is validated against the target model's catalog entry, and returns the id in validatedConfigIds; fastMode is looked up per model, and the unverified-selection path remains only for agents without a catalog. summarizeAgentRunConfigCapabilities publishes each catalogued model's effort values, still preferring legacy modelReasoningEfforts.
  • validateTurnConfigOptionValues and filterCompatibleTurnConfigOptionValues compose the target model's options through resolveAcpConfigOptionsForModel; inherited defaults are filtered against the MERGED target model (explicit create modelId, then the inherited modelId, then the model option), and an explicit create modelId drops a parent's superseded model option so the frozen Turn names one model.
  • apps/cli/AGENTS.md records the per-model invariant.

Before / after

Before After
reasoningEffort: 'extra-high' for gpt-5.4 wrote into thinking. Written into reasoning and validated against gpt's ladder.
Inherited fast/xhigh from an opus parent survived into a sonnet child. Dropped when the merged target model lacks them.
create_options listed one effort ladder for all models. Each catalogued model lists its own values.

Test plan

  • cd packages/shared && corepack pnpm exec tsgo --noEmit && corepack pnpm exec vitest run tests/acp-run-config.test.ts — 36 passed (per-model reasoning/fast mapping, catalog validation errors, no-catalog regression path, Kimi lone-toggle mapping, per-model create_options values).
  • cd apps/cli && corepack pnpm exec tsgo --noEmit && corepack pnpm exec vitest run src/commands/session.test.ts src/session/acp-session-config-applier.test.ts — 78 passed (validation against the target catalog, inherited filtering against the merged target and against the inherited model, superseded model option removal).
  • cd packages/components && corepack pnpm exec tsgo --noEmit — passed (shared is consumed as source).
  • Root corepack pnpm typecheck, corepack pnpm lint (0 errors), corepack pnpm format:check — passed. Skipped: an end-to-end MCP lody_session_create run against a live Cursor agent (mapping covered by unit tests against the observed catalog shapes).

Context handoff

Instructions for reviewing agents

  • Review focus: the reasoning branch of resolveAgentRunConfigSelection and findReasoningEffortOptionIn in packages/shared/src/acp-run-config.ts (ladder-first, toggle-fallback), and filterCompatibleInheritedTurnConfig plus dropSupersededInheritedModelOption in apps/cli/src/commands/session.ts (three-level target model chain).
  • Decisions to challenge: keeping the toggle fallback for reasoningEffort instead of rejecting it for models without a ladder; not pushing fast into validatedConfigIds (the CLI check now composes the same options).
  • Plausible failures / evidence gaps: mergeTurnDispatchConfig still replaces the whole inherited configOptionValues table with ?? (pre-existing), which narrows the inherit path to creates that carry no options of their own; unverifiedSelections has no consumer on the create path (pre-existing). Both are left as-is and worth separate issues.

Authoring context

  • User goal / directives: Make MCP-created Cursor sessions and CLI/MCP validation honor the per-model catalog introduced by the foundation PR, without touching the composer.
  • Constraints / non-goals: Keep every existing export and error wording; keep the legacy Codex modelReasoningEfforts path first; no change to mergeTurnDispatchConfig.
  • Risk-bearing decisions: Dropping a parent's superseded model option when a create names a different model; validating reasoningEffort against the catalog entry and skipping the snapshot check for that id via validatedConfigIds.
  • Destructive or irreversible behavior: None; dispatch config derivation only.
  • Deliberately not done or tested: Live MCP end-to-end run; the two pre-existing gaps named above.
  • Unknowns / confidence: High for the mapping and validation paths (each new test fails on the previous code); the inherit chain's middle term was previously untested and now has a dedicated test.

@Tigatron
Tigatron marked this pull request as ready for review September 3, 2026 10:57
@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-05T10:31:09.394352Z 301f03b New commits
ℹ️ 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.

Tigatron and others added 5 commits September 5, 2026 18:15
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>
lody_session_create takes reasoningEffort and fastMode semantically, and the
mapping onto an agent's option ids read only the probe snapshot: for a Cursor
target it could write a reasoning level into the two-valued thinking select,
reject fast mode for a model whose own catalog offers it, and publish the
probed model's effort values for every model. The mapping now reads the
target model's composed options: reasoning effort binds to a multi-level
thought_level select when the model has one and to a lone thinking toggle
otherwise, a value is validated against the target model's catalog entry and
returned in validatedConfigIds, fast mode is looked up per model, and
lody_session_create_options publishes each catalogued model's effort values.
Agents without a catalog keep the snapshot-based unverified-selection path.

Implemented with cursor-grok-4.6-xhigh-fast subagents.

Model: claude-fable-5.1
Co-authored-by: Cursor <cursoragent@cursor.com>
CLI and MCP create/chat validated configOptionValues against the probe
snapshot, so a Cursor turn targeting another model was checked against the
probed model's options: a value the target model allows could be rejected as
unknown, a value it lacks could pass, and inherited create defaults kept a
parent's opus-only fast or effort for a child that will run sonnet.
validateTurnConfigOptionValues and filterCompatibleTurnConfigOptionValues
now compose the target model's options through resolveAcpConfigOptionsForModel,
inherited defaults are filtered against the MERGED target model (explicit
create modelId, then the inherited modelId, then the model option), and an
explicit create modelId drops a parent's superseded model option so the frozen
Turn names one model.

Implemented with cursor-grok-4.6-xhigh-fast subagents.

Model: claude-fable-5.1
Co-authored-by: Cursor <cursoragent@cursor.com>
@Tigatron
Tigatron force-pushed the feat/cursor-acp-mcp-per-model-mapping branch from fefdd03 to 301f03b Compare September 5, 2026 10:26
@Tigatron

Tigatron commented Sep 5, 2026

Copy link
Copy Markdown
Author

Rebased onto the updated #344 head (3f62760) after its conflict with main was resolved; head is now 301f03b. The two PR-specific commits re-applied without conflicts and are byte-identical to the previous ones (git range-diff). The foundation commit ids in the description were updated.

Re-verified: workspace typecheck, lint (0 errors), format:check, lint:i18n, boundary checks; packages/shared tests/acp-run-config.test.ts (36 passed) and apps/cli src/commands/session.test.ts, src/session/acp-session-config-applier.test.ts, src/lib/loro/machine-document-capabilities.test.ts, tests/session-execution-service.test.ts (161 passed).

@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: 301f03bc71

ℹ️ 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".

{ signal: options.signal }
{
signal: options.signal,
...(configOptionsByModel !== undefined ? { configOptionsByModel } : {}),

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 Add the model catalog to the refresh response schema

When a registry Cursor probe returns a catalog, passing it here causes updateAcpCapabilities to return a capability containing configOptionsByModel, but AcpCapabilityCacheEntrySchema in packages/shared/src/message-schemas.ts:1206-1232 is strict and does not declare that property. Consequently, the Streams RPC parser at packages/loro-streams-rpc/src/rpc.ts:1759-1761 returns null, while the local CLI parser throws, so every successful Cursor capability refresh is reported as a timeout or invalid response instead of reaching the settings/CLI caller.

AGENTS.md reference: apps/cli/src/agent/AGENTS.md:L243-L249

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.

[Feature Request] Expose Cursor per-model config options (thinking, effort, context, fast) over ACP and make model switching reliable

1 participant