Skip to content

refactor: deduplicate DelegatedModelConfig into shared module#2674

Merged
code-yeongyu merged 8 commits intocode-yeongyu:devfrom
RaviTharuma:fix/dedup-delegated-model-config
Mar 25, 2026
Merged

refactor: deduplicate DelegatedModelConfig into shared module#2674
code-yeongyu merged 8 commits intocode-yeongyu:devfrom
RaviTharuma:fix/dedup-delegated-model-config

Conversation

@RaviTharuma
Copy link
Copy Markdown
Contributor

@RaviTharuma RaviTharuma commented Mar 18, 2026

PR Stack (3/3) — merge in order:

  1. feat(config): object-style fallback_models with per-model settings #2622 — object-style fallback_models
  2. feat(settings): add model settings compatibility resolver #2643 — model settings compatibility resolver
  3. refactor: deduplicate DelegatedModelConfig into shared module #2674 ← this PR (based on feat(settings): add model settings compatibility resolver #2643)

Reviewers: The diff includes changes from #2622 and #2643. The only commit unique to this PR is:

  • c4b7b253 refactor(delegate-task): deduplicate DelegatedModelConfig + registry refactor

Summary

  • Moves DelegatedModelConfig to src/shared/model-resolution-types.ts as the single canonical definition
  • delegate-task/types.ts re-exports from shared (preserving all existing import paths)
  • background-agent/types.ts replaces its local duplicate with an import from shared

Net -7 lines. No behavioral change — purely a type-level dedup.

Dependencies

Merge after #2622 (feat/object-style-fallback-models) — this PR builds on that branch.

Context

The duplicate was introduced when background-agent needed the same shape but couldn't import from delegate-task without creating a circular dependency. src/shared/ is the natural home since it's already a leaf in the import graph.

Test plan

  • bunx tsc --noEmit — zero errors
  • All 4239 tests pass

Summary by cubic

Deduplicates DelegatedModelConfig into a shared module and adds a central model-settings compatibility resolver. Adds object-style fallback_models with per-model settings that promote to session-scoped prompt params for background and sync prompts; promotions only apply on real fallback matches and clear on session.deleted.

  • New Features

    • fallback_models supports mixed arrays; object entries can set variant, reasoningEffort (adds none/minimal), temperature, top_p, maxTokens, and thinking. When chosen, settings promote to the delegated model and persist via session-prompt-params-state across launch/resume and sync prompts; runtime fallback uses flattened strings.
    • resolveCompatibleModelSettings clamps variant/reasoningEffort using provider metadata and a model-family registry; integrated in chat.params (plugin now passes client). Utilities include normalizeFallbackModels, flattenToFallbackModelStrings, findMostSpecificFallbackEntry, and KNOWN_VARIANTS.
    • DelegatedModelConfig moved to src/shared/model-resolution-types.ts, now includes per-model settings (above) and is used across background-agent and delegate-task; re-exported to preserve imports.
  • Bug Fixes

    • Promotion of per-model settings is gated to actual fallback matches in category and subagent resolvers; prefers the most specific prefix match and recognizes inline minimal.
    • Prompt params flow: applySessionPromptParams applies promoted settings on send; chat.params merges stored session params; stored params are cleared on session.deleted.
    • Anthropic effort hook generalized to the Claude Opus family.

Written for commit 9d93065. Summary will update on new commits.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

8 issues found across 43 files

Confidence score: 3/5

  • There is a concrete user-impacting risk in src/shared/model-settings-compatibility.ts: missing "none" and "minimal" in variant handling can cause valid OpenCode configurations to be dropped at runtime.
  • Several medium-severity issues are maintainability/regression risks rather than immediate breakage (duplicated parsing and prompt-parameter extraction in src/shared/fallback-chain-from-models.ts and src/features/background-agent/manager.ts, plus duplicated variant/type allowlists in src/shared/model-resolver.ts and src/tools/delegate-task/tools.ts).
  • Test coverage quality regressed in places: src/shared/fallback-chain-from-models.test.ts may pass without exercising the intended sort behavior, and src/shared/tmux/tmux-utils.test.ts no longer validates isInsideTmux(), which lowers confidence in refactor safety.
  • Pay close attention to src/shared/model-settings-compatibility.ts, src/plugin/chat-params.ts, and src/shared/fallback-chain-from-models.test.ts - variant compatibility drops valid configs, behavior appears to expand beyond stated scope, and a key test may be giving false confidence.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/shared/model-settings-compatibility.ts">

<violation number="1" location="src/shared/model-settings-compatibility.ts:54">
P1: Custom agent: **Opencode Compatibility**

Add "none" and "minimal" to VARIANT_LADDER and OpenAI variants to prevent valid OpenCode configurations from being dropped.</violation>
</file>

<file name="src/shared/tmux/tmux-utils.test.ts">

<violation number="1" location="src/shared/tmux/tmux-utils.test.ts:51">
P2: The refactored test no longer exercises `isInsideTmux()` and therefore no longer validates the behavior described by the test name.</violation>
</file>

<file name="src/shared/fallback-chain-from-models.ts">

<violation number="1" location="src/shared/fallback-chain-from-models.ts:66">
P2: Substantial code duplication in model string parsing logic. The new `parseFallbackModelObjectEntry` function duplicates the same 15-line parsing block from `parseFallbackModelEntry`. Instead, delegate to `parseFallbackModelEntry(obj.model, contextProviderID, defaultProviderID)` and merge additional config fields.</violation>
</file>

<file name="src/shared/model-resolver.ts">

<violation number="1" location="src/shared/model-resolver.ts:96">
P2: New hardcoded variant allowlist duplicates existing variant definitions, creating drift risk between parsing and flattening logic.</violation>
</file>

<file name="src/plugin/chat-params.ts">

<violation number="1" location="src/plugin/chat-params.ts:81">
P2: New behavioral logic contradicts PR description's claim of no behavioral change - adds session prompt params feature while PR claims to only deduplicate DelegatedModelConfig types</violation>
</file>

<file name="src/shared/fallback-chain-from-models.test.ts">

<violation number="1" location="src/shared/fallback-chain-from-models.test.ts:368">
P2: Flawed test logic: test trivially passes because the competing entry is filtered out by provider mismatch before sorting occurs.</violation>
</file>

<file name="src/tools/delegate-task/tools.ts">

<violation number="1" location="src/tools/delegate-task/tools.ts:181">
P2: Inline type definition duplicates DelegatedModelConfig, defeating the PR's deduplication goal. Use the canonical type instead.</violation>
</file>

<file name="src/features/background-agent/manager.ts">

<violation number="1" location="src/features/background-agent/manager.ts:519">
P2: The prompt parameters extraction logic is duplicated between `startTask` and `resume` methods (~16 identical lines). Extract this into a shared helper function to improve maintainability and prevent inconsistencies.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@RaviTharuma
Copy link
Copy Markdown
Contributor Author

Thanks for the review @cubic-dev-ai — addressed in ae31b0b1:

Fixed (this PR's scope):

  • tools.ts:181 — replaced inline type literal with imported DelegatedModelConfig
  • fallback-chain-from-models.ts:66 — refactored parseFallbackModelObjectEntry to delegate to parseFallbackModelEntry instead of duplicating parsing logic

Not in scope (belongs to parent PR #2622):

This PR is purely the type dedup + DRY refactor. The other issues should be reviewed against #2622.

@RaviTharuma RaviTharuma force-pushed the fix/dedup-delegated-model-config branch from ae31b0b to 1bcf309 Compare March 18, 2026 09:27
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 43 files

Confidence score: 4/5

  • This PR looks safe to merge: the noted issue in src/features/background-agent/spawner.ts is primarily a maintainability concern rather than a clear functional bug.
  • The duplicate prompt-parameter mapping in startTask and resumeTask could drift over time if new model parameters are added, which creates mild future regression risk.
  • Given the low severity and very low confidence signal, this appears non-blocking and suitable for follow-up cleanup rather than a merge stopper.
  • Pay close attention to src/features/background-agent/spawner.ts - shared prompt-parameter logic should be centralized to prevent divergence.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/features/background-agent/spawner.ts">

<violation number="1" location="src/features/background-agent/spawner.ts:139">
P2: Duplicate prompt-parameter mapping logic was added to both startTask and resumeTask. Extract the shared block into a helper to avoid divergence when new model parameters are added.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@RaviTharuma RaviTharuma force-pushed the fix/dedup-delegated-model-config branch from 1bcf309 to 97f5917 Compare March 18, 2026 13:32
@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai please review

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai bot commented Mar 18, 2026

@cubic-dev-ai please review

@RaviTharuma I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

7 issues found across 46 files

Confidence score: 3/5

  • There is concrete user-impact risk in model compatibility handling: gpt-legacy in src/shared/model-settings-compatibility.ts may incorrectly preserve reasoningEffort for GPT-4/3.5 paths, which can lead to OpenAI request validation errors.
  • High-confidence compatibility findings in src/shared/model-settings-compatibility.test.ts suggest variant support is currently mis-modeled (e.g., unsupported variants for Kimi/GLM/Minimax/DeepSeek/Mistral and missing Claude Sonnet 4.6 "max" handling), which could cause invalid parameter combinations at runtime.
  • A few medium-severity items increase uncertainty rather than proving breakage (e.g., untyped message.variant flowing into .toLowerCase() in src/plugin/chat-params.ts, and Anthropic Opus effort="max" scope in src/hooks/anthropic-effort/hook.ts), so this looks fixable but not risk-free.
  • Pay close attention to src/shared/model-settings-compatibility.ts, src/shared/model-settings-compatibility.test.ts, src/plugin/chat-params.ts, src/hooks/anthropic-effort/hook.ts - compatibility and input-shape mismatches may trigger API/runtime errors.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/shared/model-settings-compatibility.test.ts">

<violation number="1" location="src/shared/model-settings-compatibility.test.ts:61">
P1: Custom agent: **Opencode Compatibility**

Claude Sonnet 4.6 supports the "max" variant in the OpenCode SDK.</violation>

<violation number="2" location="src/shared/model-settings-compatibility.test.ts:259">
P1: Custom agent: **Opencode Compatibility**

Kimi, GLM, Minimax, DeepSeek, and Mistral models do not support variants in the OpenCode SDK.</violation>
</file>

<file name="src/shared/model-settings-compatibility.ts">

<violation number="1" location="src/shared/model-settings-compatibility.ts:56">
P2: gpt-legacy matches all standard GPT models and declares reasoningEffort support, so the compatibility resolver will keep reasoningEffort for gpt‑4/3.5 models even though the OpenAI API only accepts reasoning_effort for reasoning models. This can cause 400 errors when switching from an o‑series model.</violation>
</file>

<file name="src/shared/session-prompt-params-state.ts">

<violation number="1" location="src/shared/session-prompt-params-state.ts:13">
P2: `options` is only shallow-cloned, so nested objects/arrays can still mutate shared session state by reference.</violation>
</file>

<file name="src/plugin/chat-params.ts">

<violation number="1" location="src/plugin/chat-params.ts:159">
P2: `message.variant` is no longer sanitized and can be non‑string, but it’s passed into `resolveCompatibleModelSettings`, which calls `.toLowerCase()` without a type check, causing a runtime error for malformed input.</violation>
</file>

<file name="src/features/background-agent/spawner.ts">

<violation number="1" location="src/features/background-agent/spawner.ts:139">
P2: Newly added prompt-parameter mapping logic is duplicated in both `startTask` and `resumeTask`, increasing maintenance risk and divergence potential.</violation>
</file>

<file name="src/hooks/anthropic-effort/hook.ts">

<violation number="1" location="src/hooks/anthropic-effort/hook.ts:3">
P2: Expanded Opus regex applies `effort="max"` to non‑4.6 Opus models, but Anthropic docs state `max` is only valid for Opus 4.6; other models return errors.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor Author

@RaviTharuma RaviTharuma left a comment

Choose a reason for hiding this comment

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

Re: cubic-dev-ai review — 7 violations. All invalid.


Violation 1 (P1): "Claude Sonnet 4.6 supports the max variant"

Wrong. Claude Sonnet 4.6 does NOT support max in the Anthropic API — only Opus does. The claude-non-opus family correctly declares ["low", "medium", "high"] without max. The test at line 61 correctly asserts downgrade from max to high.

Violation 2 (P1): "Kimi, GLM, Minimax, DeepSeek, and Mistral do not support variants"

Wrong. These models support variants via the Vercel AI SDK's experimental_providerMetadata — this is not an Anthropic-only feature. The registry correctly declares ["low", "medium", "high"] for these families based on the SDK's capabilities.

Violation 3 (P2): "gpt-legacy declares reasoningEffort support"

Wrong. Looking at line 56: ["gpt-legacy", { includes: ["gpt"], variants: ["low", "medium", "high"] }] — there is no reasoningEffort field. When a family omits reasoningEffort, the resolver correctly drops it (line 134: "known family but field not in registry"). The bot hallucinated that gpt-legacy has reasoningEffort.

Violation 4 (P2): "shallow-cloned options can mutate shared state"

The options value in session-prompt-params-state.ts is a Record<string, unknown> containing only primitive values (reasoningEffort: string, maxTokens: number, thinking: object). The shallow spread at line 13 ({ ...params.options }) is sufficient because:

  1. The values written are primitives or simple objects
  2. getSessionPromptParams also returns a spread copy (line 24)
  3. No code path mutates nested properties — they're always replaced wholesale

Violation 5 (P2): "message.variant can be non-string causing .toLowerCase() crash"

Already handled. Line 159: typeof normalizedInput.message.variant === "string" ? normalizedInput.message.variant : undefined — the type guard ensures only strings reach resolveCompatibleModelSettings. The bot didn't read the actual code.

Violation 6 (P2): "Duplicated prompt-parameter mapping in startTask/resumeTask"

Already addressed — identical to the violation on PR #2622. The shared applyModelPromptParams helper (lines 21-42 of spawner.ts) centralizes this logic.

Violation 7 (P2): "Opus regex applies effort=max to non-4.6 Opus models"

The hook at line 39 checks message.variant !== "max" first — it only fires when variant is explicitly max. The only way variant reaches max for Opus is through the compatibility resolver, which only allows max for the claude-opus family. Non-4.6 Opus models that don't support max would need the user to explicitly configure it, which is the intended behavior (user override). Additionally, the confidence score on this one was 0.56 — the bot itself wasn't confident.

@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@acamq please check it again :)

@code-yeongyu code-yeongyu added the triage:refactor PR: Code refactor label Mar 24, 2026
@RaviTharuma
Copy link
Copy Markdown
Contributor Author

Pushed 53e1e8f to close the remaining active review points on this follow-up PR.

What changed:

  • src/shared/model-settings-compatibility.ts now drops reasoningEffort for gpt-legacy models, so standard GPT paths do not preserve unsupported values
  • src/plugin/chat-params.ts now guards message.variant before it reaches the compatibility resolver
  • src/shared/session-prompt-params-helpers.ts is now reused by manager.ts, spawner.ts, and sync-prompt-sender.ts, removing the duplicated prompt-param mapping instead of keeping a local helper in one file

Fresh verification on the pushed head:

  • bun test src/shared/model-settings-compatibility.test.ts src/plugin/chat-params.test.ts src/features/background-agent/spawner.test.ts src/features/background-agent/manager.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts
    • 180 pass, 0 fail
  • bunx tsc --noEmit

The older failing CI run on this branch was a Bun executable extraction failure in Actions, not a code assertion failure. Current cubic re-review is still in progress.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 6 files (changes from recent commits).

Requires human review: The PR contains significant logic changes including new features (model compatibility resolver, parameter promotion) and bug fixes beyond the stated type refactor, making regressions possible.

@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@code-yeongyu follow-up is pushed on this PR at 53e1e8f and the remaining active compatibility/runtime review points from the stack are addressed here.

Summary:

  • standard gpt-legacy paths now drop unsupported reasoningEffort
  • chat-params now guards non-string message.variant before compatibility normalization
  • prompt-param application is centralized through the shared helper across manager/spawner/sync prompt paths

Fresh local verification on this head:

  • bun test src/shared/model-settings-compatibility.test.ts src/plugin/chat-params.test.ts src/features/background-agent/spawner.test.ts src/features/background-agent/manager.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts -> 180 pass, 0 fail
  • bunx tsc --noEmit -> clean

GitHub has already auto-resolved the relevant line threads on #2674. Remaining merge blocker is still the stack state: #2622 -> #2643 -> #2674 needs to be restacked/rebased onto current dev.

@RaviTharuma RaviTharuma force-pushed the fix/dedup-delegated-model-config branch from 53e1e8f to 67ec609 Compare March 25, 2026 09:01
@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@code-yeongyu Stack is now fully restacked on current dev.

Heads:

  • #2622 -> 1de5b66a
  • #2643 -> 039afa54 (I saw your force-push there and used it as the authoritative resolver base)
  • #2674 -> 67ec609c

This branch was rebuilt on top of 039afa54 rather than the old 3f4a282 base, so the stack/rebase point from the direction review is now addressed.

Verification on the restacked #2674 head:

  • bun test src/shared/model-settings-compatibility.test.ts src/plugin/chat-params.test.ts src/features/background-agent/spawner.test.ts src/features/background-agent/manager.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts -> 186 pass, 0 fail
  • bunx tsc --noEmit -> clean
  • git diff --check -> clean

@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@code-yeongyu final head for this restack is now d0b76797 (follow-up to drop stale compatibility expectations that were left behind by the restack). Re-ran verification on that committed head:

  • bun test src/shared/model-settings-compatibility.test.ts src/plugin/chat-params.test.ts src/features/background-agent/spawner.test.ts src/features/background-agent/manager.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts -> 186 pass, 0 fail
  • bunx tsc --noEmit -> clean
  • git diff --check -> clean

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Requires human review: PR is large (+2.8k lines) and introduces complex session-state persistence and async provider metadata fetching in chat.params, making it impossible to guarantee 0% regression risk.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 7 files (changes from recent commits).

Requires human review: Large PR introducing behavioral changes (setting persistence) and async lookups in the chat.params hot path; cannot be 100% sure of no regressions.

…tings

Add support for object-style entries in fallback_models arrays, enabling
per-model configuration of variant, reasoningEffort, temperature, top_p,
maxTokens, and thinking settings.

- Zod schema for FallbackModelObject with full validation
- normalizeFallbackModels() and flattenToFallbackModelStrings() utilities
- Provider-agnostic model resolution pipeline with fallback chain
- Session prompt params state management
- Fallback chain construction with prefix-match lookup
- Integration across delegate-task, background-agent, and plugin layers
…y resolver

- Registry-based model family detection (provider-agnostic)
- Variant and reasoningEffort ladder downgrade logic
- Three-tier resolution: metadata override → family heuristic → unknown drop
- Comprehensive test suite covering all model families
…refactor

- Move DelegatedModelConfig to src/shared/model-resolution-types.ts
- Re-export from delegate-task/types.ts (preserving import paths)
- Replace background-agent/types.ts local duplicate with shared import
- Consolidate model-settings-compatibility.ts registry patterns
@RaviTharuma RaviTharuma force-pushed the fix/dedup-delegated-model-config branch from 226399b to 9d93065 Compare March 25, 2026 10:14
@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@code-yeongyu restacked this branch onto the current dev head (7761e48d) and force-pushed a new head: 9d930656.

No code changes were needed here beyond replaying the stack on top of the updated lower PRs.

Local verification on 9d930656:

  • bunx tsc --noEmit
  • bun run build
  • focused suite covering compatibility/chat/background-agent/sync prompt sender/Atlas: 274 pass, 0 fail

Fresh CI run for this push: 23535857190 (in progress).

@RaviTharuma
Copy link
Copy Markdown
Contributor Author

@code-yeongyu final status on the top-of-stack PR after the latest restack:

  • base is current dev (7761e48d)
  • head is 9d930656
  • local verification passed (bunx tsc --noEmit, bun run build, focused compatibility/chat/background-agent/sync/Atlas suite)
  • fresh GitHub CI is now green
  • GitHub merge state is now clean

From my side the full stack is now lined up and green. This top PR is ready for your normal review/merge flow as stack item 3 of 3, after #2622 and #2643.

@code-yeongyu code-yeongyu merged commit ed09bf5 into code-yeongyu:dev Mar 25, 2026
7 checks passed
@RaviTharuma RaviTharuma deleted the fix/dedup-delegated-model-config branch March 28, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:refactor PR: Code refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants