Skip to content

🤖 feat: route skills to model classes (Settings-managed large/medium/small) - #3849

Open
asm wants to merge 17 commits into
coder:mainfrom
asm:skill-model-classes
Open

🤖 feat: route skills to model classes (Settings-managed large/medium/small)#3849
asm wants to merge 17 commits into
coder:mainfrom
asm:skill-model-classes

Conversation

@asm

@asm asm commented Aug 14, 2026

Copy link
Copy Markdown

Summary

Skills can now be routed to user-defined model size classes so mechanical skills (session wrap-up, worktree helpers, PR chores) don't consume frontier-model tokens. Classes map a name to a model[+thinking] value (one-shot syntax) and are edited in Settings → Models → Model Classes; skills bind to a class via the spec-standard frontmatter metadata: model-class: small or a local skillModelClasses config table. The class model applies to that invocation only — the workspace model is untouched. One-shot overrides also compose with skill invocations now (/haiku+0 /done), and an explicit one-shot always beats class routing.

Background

Models churn constantly, so per-skill bindings shouldn't name concrete models — they name a class (large/medium/small), and only the class map names models. Updating one class re-routes every bound skill.

  • 🤖 feat: add /<model> one-shot model override syntax #2142 introduced one-shot model overrides; this reuses that exact plumbing (per-send model) and extends it to compose with skill slash invocations.
  • Agent definition ai.model is not consulted when spawning sub-agent tasks #3038 describes the kindred gap for agent definitions (ai.model parsed but not consulted); this PR takes the same position for skills — a declared model preference should be honored — while keeping it strictly opt-in.
  • Portability: the binding uses the Agent Skills spec's metadata map, which other harnesses ignore. Frontmatter bindings to a class the user never defined are deliberately inert, so skills shipping metadata: model-class can never break users who haven't opted in. The config table exists for routing skills the user doesn't own — and because the table is the user's own explicit intent, a dangling table entry (naming a class that was deleted) fails loudly instead of silently unrouting.

Implementation

  • Config: modelClasses and skillModelClasses records (schema, load normalization, saveConfig whitelist, config.updateModelClasses route). Maps are stored verbatim — entries this build can't parse are preserved, not dropped, so edits from an older/newer build never destroy classes they don't understand. Validity is judged lazily at send time by the resolver.
  • Shared resolver (src/common/utils/ai/skillModelClasses.ts): binding resolution as a discriminated union (unbound / unknown-class / invalid-value / resolved), plus isModelServableWithProvidersConfig (modelAvailability.ts) wrapping the routing layer's isModelAvailable with the same exported provider/gateway predicates useRouting consumes — so a model reachable only via a configured gateway (e.g. OpenRouter) correctly counts as available, route-priority membership is honored, and the editor warning cannot drift from the send-time gate.
  • Send path (AgentSession.sendMessage): the override is resolved before the pricing gate, PDF-support preflight, and any history mutation, so those gates evaluate the model that will actually stream and a broken binding errors before persisting side effects. Routing is gated by a dedicated skipSkillModelRouting send option (set by explicit one-shot composition and compaction retries) rather than overloading skipAiSettingsPersistence. Bound-but-broken mappings (dangling table entry, invalid value, no configured route for the model) fail the send with an actionable error naming the fix and the one-shot bypass; unbound skills take a null fast-path and infrastructure failures (unreadable skill/config, providers state unavailable) fail open.
  • Compaction interplay: the auto-compaction threshold is computed against the routed model's context window, while the compaction request itself and its follow-up resume options carry the pre-routing model/thinking (the compaction model must fit the uncompacted history, and the user's model choice must survive the round-trip). Mid-stream forced compaction during a routed turn threads the same pre-routing options through the stream context. Routed sends only auto-compact when the history is within ROUTED_SEND_COMPACTION_HEADROOM_PERCENT (10 points) of the routed model's window — headroom for the pending turn, while still far above the workspace threshold so a small-context class model can't trigger surprise compaction of a history the workspace model handles fine.
  • Settings UI: a "Model Classes" section under Settings → Models with fixed canonical slots (large/medium/small — a shared vocabulary keeps skill frontmatter portable across machines), model + thinking selects per class, custom hand-edited classes preserved on save and listed read-only (unparseable raw values shown in a tooltip), and an inline "no configured route can serve this model" warning using the same predicate as the send-time check. Edits are disabled until config and routing state finish loading, so an early click can't clobber persisted classes; thinking suffixes carry across model swaps only when the target model's policy supports them.
  • Composer: parseCommandWithSkillInvocation composes a leading one-shot with a skill invocation by re-running parseCommand on the one-shot's message — registered commands and nested one-shots stay out of skill resolution, mirroring direct-invocation semantics exactly. Composed sends record the full command prefix (model /skill) in message metadata so transcript badges render what was actually typed. Numeric one-shot thinking is model-relative, so a thinking-only composed send (/+0 /skill) also passes the raw index (oneShotThinkingIndex) for the backend to re-resolve against the routed model's ladder — +0 means the class model's lowest level, not the workspace model's. Compact-and-retry rebuilds re-derive the one-shot's model and thinking from the original text (with skipAiSettingsPersistence, so a re-dispatch never persists one-shot values as new workspace defaults), and prepareCompactionMessage keeps carried one-shot fields from being clobbered by ambient stored options.
  • Attribution: when routing applies, the persisted user-message metadata is re-stamped with the routed model (requestedModel), so the pending-turn label and history consumers see the model that actually streams.

Review-round hardening

Sixteen Codex review rounds tightened the edges (all threads resolved):

  • Send-path ordering: routing resolves before the pricing gate, PDF preflight, and any history mutation; rejected manual/queued sends persist a visible error (never for edits, which return bare and restore the draft); queued PDF rejections surface instead of vanishing.
  • Compaction interplay: routed sends compact within a headroom of the routed window (pre-send AND mid-stream); the compaction request runs on whichever of the user/routed model has the larger usable window; the routed policy survives same-session retries, compact-and-retry rebuilds (model, thinking, prefix, skipAiSettingsPersistence), and process relaunch (durable compactionBaseOptions in retrySendOptions, honored even in child task workspaces).
  • Availability truth: the shared servability predicate and ProviderModelFactory.resolveModelRoute both apply model-aware OpenAI credential rules (Codex-OAuth-only serves the OAuth set; API keys attempt anything; custom openai-compatible providers shadowing the openai id are exempt).
  • Telemetry attribution: the accepted-send payload reports routedModel + post-floor routedThinkingLevel; persisted metadata re-stamps requestedModel. Queued-send event attribution is documented as a follow-up (needs backend-side event capture).
  • Editor integrity: class edits persist before publishing (no split-brain with a fast follow-up send), rows lock while their write is in flight, custom classes survive verbatim, and CI snapshots the wrapping layout at a pinned phone viewport.

Validation

  • ~70 tests across the feature: resolver statuses (frontmatter-inert vs table-loud, blank table entries, the opt-in guard), availability predicate (route-priority membership, disabled providers), config round-trip through the saveConfig whitelist (including preservation of unknown classes), end-to-end AgentSession routing and error paths via the session harness (gate ordering, skipSkillModelRouting exemption, thinking-only bindings, compaction follow-up model), composition parser cases, and editor UI behavior (clear preserves custom classes; load gating; warning states).
  • Full bun test src failure set is identical to main's on the same machine (pre-existing env-sensitive tests only).
  • Verified live in Storybook (ModelsSection stories now seed classes, including one pointing at an unconfigured provider to exercise the warning; row layout wraps at mobile widths) and in a packaged build used for daily work.

Risks

The sensitive area is the insertion in AgentSession.sendMessage. Scope is tightly bounded: only sends carrying agent-skill metadata without skipSkillModelRouting are considered, and workspaces with no modelClasses/table binding hit an early return before any skill read — no behavior change for anyone who hasn't opted in. Compaction interplay (threshold on the routed model, compaction request and mid-stream forced compaction on the user's model, follow-up resume options) is covered by tests. One known asymmetry, documented at the helper: the shared servability predicate mirrors the routing layer's gateway/priority gates but not per-request policy checks, so an editor warning can under-report in exotic policy setups — the send-time error remains authoritative.


🤖 Generated with Claude Code

@asm
asm marked this pull request as draft August 14, 2026 00:08
@asm
asm marked this pull request as ready for review August 14, 2026 03:20
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

@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: 60f19ad5e5

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

Comment thread src/browser/features/ChatInput/index.tsx
Comment thread src/browser/hooks/useCompactAndRetry.ts Outdated
Comment thread src/node/services/agentSession.ts
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

All three findings addressed in b1b0bf8:

  • Numeric thinking vs routed model: the frontend now passes the raw index (oneShotThinkingIndex send option) alongside the workspace-resolved level, and AgentSession re-resolves it against the routed class model when routing applies — /+0 /skill means the routed model's lowest allowed level. Covered by a routing test where the pre-resolved level ("medium") and the routed ladder ("off") differ.
  • One-shot thinking across compact-and-retry: the rebuilt follow-up carries the parsed thinking (named as-is; numeric resolved against the explicit model, or kept as a raw index for routed re-resolution) plus skipAiSettingsPersistence, and prepareCompactionMessage no longer lets ambient stored options clobber carried one-shot fields. This also fixes a latent issue: without the persistence flag, the re-dispatch would have persisted the one-shot model as the new workspace default.
  • requestedModel: when routing applies, the persisted user-message metadata is re-stamped with the routed model, so the incoming user event and history consumers attribute the send correctly. One deliberate limit: the frontend's fire-and-forget messageSent telemetry event still reports the requested model — threading the routed model through the send result would widen Result<void> across ~15 return sites, which felt too invasive here; happy to do it as a follow-up if maintainers prefer.

@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: b1b0bf8591

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

Comment thread src/node/services/agentSession.ts
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-2 finding addressed in 6c4903d: routed sends now compact within ROUTED_SEND_COMPACTION_HEADROOM_PERCENT (10 points) of the routed model's window instead of requiring a full 100% — headroom for the pending message, attachments, and skill snapshot that the recorded usage doesn't include, while still staying far above the workspace threshold so a cheap skill invocation can't force an unrequested compaction of a history that fits.

@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: 6c4903deb0

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

Comment thread src/node/services/agentSession.ts
Comment thread src/node/services/agentSession.ts Outdated
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Both round-3 findings addressed in 297b210:

  • Mid-stream routed policy: checkMidStream now accepts a force-threshold override, and routed turns (identified by the stream context's compaction base options) pass the same routed-send headroom bar — a usage update during a routed turn no longer forces compaction at the workspace threshold+buffer against the smaller routed window. Monitor test covers the override at 75% (no trigger) and 92% (trigger).
  • Compaction model fit: on-send and mid-stream compaction now run with whichever of the user's / routed model has the larger usable context window (getEffectiveContextLimit comparison) — normally still the user's model, but a class routing UP past the user's window no longer summarizes on a model that can't read the history. The deferred follow-up keeps pre-routing options and re-routes at dispatch either way.

@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: 297b210330

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

Comment thread src/common/utils/ai/modelAvailability.ts Outdated
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-4 finding addressed in 50b68ee: added canDirectOpenAIServeModel (colocated with the existing Codex OAuth routing mirrors) reflecting the factory's credential selection — OAuth-required models need stored tokens even with an API key, and OAuth-only configs serve only the allowed model set — and the shared servability predicate now consults it for direct-OpenAI routes. An OAuth-ineligible class model no longer passes the preflight on a Codex-OAuth-only config; a later gateway in routePriority can win, or the user gets the actionable class error. Tests cover OAuth-only vs API-key vs OAuth-required combinations.

@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: 50b68ee8fc

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

Comment thread src/common/utils/ai/modelAvailability.ts
Comment thread src/node/services/agentSession.ts
Comment thread src/node/services/agentSession.ts
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

All three round-5 findings addressed in 6452b8a:

  • Factory route selection: resolveModelRoute (both call sites) now passes the canonical model into isProviderAvailableForRouting, which rejects direct OpenAI when tokens-only credentials can't serve the model — a usable gateway later in routePriority wins, matching the shared predicate. I also realigned canDirectOpenAIServeModel with the factory's actual fallback semantics (an API key attempts any model, including OAuth-preferred ones; tokens-only serves only the allowed set) and updated the tests accordingly.
  • PDF preflight: the client-side check now defers to the backend's routed-model gate whenever a routable skill invocation is present (skillInvocation && !modelOverride) — the backend validates against the class model and rejects with a persisted, visible error, so a PDF-capable class model bound to a skill can actually receive PDFs.
  • Send telemetry: sendMessage now returns SendMessageAccepted { routedModel } through AgentSession → WorkspaceService → router → wire schema, and ChatInput attributes messageSent telemetry to result.data?.routedModel ?? effectiveModel. Queued sends report no routed model (dispatch happens later) and fall back to the requested model, as documented on the schema. Routing tests assert the payload for both the routed and skip-flag cases.

@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: 6452b8a491

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

Comment thread src/browser/hooks/useModelClasses.ts Outdated
Comment thread src/browser/features/Settings/Sections/ModelClassesEditor.tsx
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Both round-6 findings addressed in 47afc04:

  • Manual memoization removed: the subscription's fetch now lives inside the effect and the write-failure revert reaches it through a ref — no useCallback, no exhaustive-deps suppressions. (Note: useModelFallbacks, which this hook was modeled on, uses the same pre-existing useCallback pattern upstream; left untouched here as out of scope.)
  • Pinned phone snapshot: ModelsConfiguredPhone pins a Pixel phone matrix variant mirrored with globals.viewport, so CI snapshots the Model Classes rows at the width their wrapping layout exists for. Verified live at 375px: label/select wrap, inline no-route warning on the unconfigured row, no right-edge overflow.

@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: 47afc04612

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

Comment thread src/browser/hooks/useModelClasses.ts Outdated
Comment thread src/browser/features/ChatInput/index.tsx Outdated
Comment thread src/browser/features/ChatInput/index.tsx Outdated
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-7 findings in 44facc0:

  • Split-brain class edits (fixed): useModelClasses now serializes writes and publishes state only on the write's ack — the editor can no longer advertise a mapping the backend doesn't have. Rapid edits build on the newest pending intent (no lost updates), and failures still revert via refetch.
  • Routed thinking telemetry (fixed): the accepted-send payload gains routedThinkingLevel (class suffix or re-resolved numeric one-shot), and messageSent attributes to it. Covered by a routing test asserting both payload fields.
  • Queued-send attribution (descoped as follow-up, with rationale): routing resolves at dispatch, not at queue-accept, so correct attribution for queued sends requires emitting the event backend-side at dispatch — and message_sent carries frontend-only provenance (frontendPlatform, runtime context) that a backend emitter would misreport. The durable record is already attributed correctly at dispatch via the persisted requestedModel stamp; only the fire-and-forget analytics event stays approximate for the queued minority. Happy to build dispatch-time backend capture as a follow-up if maintainers want it — it needs its own provenance design rather than a bolt-on here.

@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: 44facc03ea

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

Comment thread src/node/services/agentSession.ts Outdated
Comment thread src/node/services/agentSession.ts
Comment thread src/browser/features/ChatInput/index.tsx Outdated
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

All three round-8 findings addressed in 79fb8e0:

  • Queued PDF rejections preserved: both PDF gate branches (unsupported input, size limit) now persist and surface the rejection through preserveRejectedManualSend — same contract as the pricing and model-class gates — so a queued skill send whose PDF the routed model rejects leaves a visible transcript error instead of silently discarding the user's text and attachment.
  • Post-policy routed thinking: the per-model floor resolution + clamping now live in one shared method (resolveThinkingFloorForModel / enforceThinkingFloorsForModel) used by both the stream request build and the accepted-send payload — routedThinkingLevel reports the clamped level the stream actually runs at.
  • Named one-shot fallback: messageSent falls back to the send's actual sendOptions.thinkingLevel (which carries a composed one-shot's thinking) rather than the ambient workspace setting.

@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: 79fb8e040b

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

Comment thread src/node/services/agentSession.ts Outdated
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-9 finding addressed in 6284377: routedThinkingLevel now reports the effective level for every routed send — whatever optionsForStream carries (class suffix, re-resolved numeric one-shot, or a named/ambient level riding through), clamped by the shared per-model floor enforcement. A /+off /skill routed onto a floor-medium model reports medium. Test covers the ride-through case.

@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: 62843778d0

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

Comment thread src/node/services/agentSession.ts
Comment thread src/browser/hooks/useCompactAndRetry.ts Outdated
@asm
asm force-pushed the skill-model-classes branch from 6284377 to 3d6ffbd Compare August 14, 2026 17:57
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Both round-10 findings addressed, and the branch is rebased onto latest main (the #3844 conflict in agentSession.ts resolved by adopting the new gateway-preserving lookupMinThinkingLevelOverride inside the shared floor helper):

  • Routed compaction context across retries: the auto-retry resume state now carries compactionBaseOptions, resumeStream threads it through to streamWithHistory, and the post-compaction context-exceeded retry reads it from the captured stream context — same-session restarts keep both the routed force threshold and the larger-window compaction model selection.
  • Leading-whitespace one-shots: the compact-retry reparse guard now trims before checking, matching parseCommand's own tolerance.

@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: 3d6ffbd18d

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

Comment thread src/browser/hooks/useCompactAndRetry.ts
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-11 finding addressed: the compact-and-retry metadata rebuild now carries source.commandPrefix into buildAgentSkillMetadata, so recovered composed invocations keep their command badge.

@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: f9eb115404

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

Comment thread src/common/utils/ai/modelAvailability.ts
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-12 finding addressed: canDirectOpenAIServeModel now recognizes a custom openai-compatible provider shadowing the openai id (via the existing isCustomOpenAICompatibleProviderConfig detector) and exempts it from built-in OpenAI credential rules — custom endpoints authenticate on their own terms, so availability falls back to the ordinary isConfigured gate. Test covers a keyless shadowing provider serving an OAuth-ineligible model.

@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: 2ecc3f4b18

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

Comment thread src/node/services/agentSession.ts
Comment thread src/node/services/agentSession.ts
Comment thread src/browser/features/Settings/Sections/ModelClassesEditor.tsx
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

All three round-13 findings addressed:

  • Rejected edits: the class-routing and PDF gates skip preserveRejectedManualSend when options.editMessageId is set — the error returns bare, the original message stays untouched, and the browser restores the edit draft. Preservation remains for dequeued sends.
  • Rapid class edits: useModelClasses exposes per-class pending-write counts and the editor disables a row's controls until its write acks, so a second edit can never compose from the still-unpublished old value (the disable-while-pending remedy suggested in the finding, consistent with round 11's persist-before-publish).
  • Durable routed compaction context: pickStartupRetrySendOptions embeds a one-level durable compactionBaseOptions pick for routed turns, and deriveStartupAutoRetryRequest's consumer destructures it into the resume state (never into the replayed send options) — the routed force bar and larger-window compaction base survive a relaunch. Absent on rows from older versions ⇒ today's behavior, self-healing.

@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: 642c4389c4

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

Comment thread src/node/services/agentSession.ts
Comment thread src/node/services/agentSession.ts
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Both round-14 findings addressed:

  • Persisted context restored: deriveStartupAutoRetryRequest now copies retrySendOptions.compactionBaseOptions into the retry request, completing the relaunch path — the routed compaction policy survives process exit end-to-end.
  • Pricing gate edit guard: the budgeted-goal gate skips preserveRejectedManualSend when editMessageId is set, matching the class-routing and PDF gates.

@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: d35a3c78fd

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

Comment thread src/node/services/agentSession.ts
asm and others added 16 commits August 14, 2026 12:54
…), compose one-shots with skill invocations

Skills bound to a model class (frontmatter metadata model-class, or the
skillModelClasses config table) stream on the class's model for that send
only. Classes are edited in Settings -> Models -> Model Classes; broken
bindings fail the send with actionable errors; /model+thinking one-shots
compose with skill invocations and bypass class routing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Send path:
- Resolve the skill model-class override before the pricing gate, PDF
  preflight, and history mutations so a broken binding can no longer
  charge gates against the wrong model or persist side effects before
  erroring.
- Route compaction follow-ups with the pre-routing options so a routed
  skill send that triggers auto-compaction resumes on the original
  model/thinking, and only compact routed sends at >=100% usage.
- Add a dedicated skipSkillModelRouting wire flag instead of
  overloading skipAiSettingsPersistence; one-shot model overrides set
  it explicitly and compaction retries re-derive it from the original
  message text.
- One-shot composed sends now carry the full command prefix in
  muxMetadata so transcript badges render the model prefix.

Binding semantics:
- Frontmatter bindings to an unconfigured class are inert (portable
  skills can ship model-class metadata without breaking sends); a
  dangling skillModelClasses table entry still errors loudly since the
  table is the user's explicit routing intent. Blank table entries are
  treated as unbound.

Settings/editor:
- Store model-class maps verbatim instead of sanitizing away entries
  this build cannot parse, so edits from an older/newer build no longer
  destroy unknown classes.
- Gate class edits and the unroutable-model warning on config/routing
  load completion to avoid clobbering state during the initial fetch.
- Carry thinking suffixes across model swaps only when the new model's
  policy supports them; show raw invalid values in a tooltip.
- Share provider/gateway servability predicates between the editor
  warning and the send-time gate so the two can't drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rides through retry

Codex review round 1:

- Numeric one-shot thinking is model-relative, but the frontend resolves
  it against the workspace model before routing is known. Pass the raw
  index (oneShotThinkingIndex) and re-resolve it against the routed
  class model in AgentSession, so "/+0 /skill" means the routed model's
  lowest level rather than the workspace model's.
- Compact-and-retry rebuilds now carry the one-shot's thinking (named
  resolved as-is, numeric resolved against the explicit model or kept
  as a raw index for routed re-resolution) and skipAiSettingsPersistence,
  and prepareCompactionMessage stops letting ambient stored options
  clobber carried one-shot fields — without the persistence flag the
  re-dispatch would also have persisted the one-shot model as the new
  workspace default.
- Preserve skipSkillModelRouting and oneShotThinkingIndex in
  pickPreservedSendOptions so backend-built compaction follow-ups keep
  one-shot semantics too.
- Stamp the routed model into the persisted user-message metadata
  (requestedModel) so the pending-turn label and history consumers
  attribute routed sends to the model that actually streams.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review round 2: the >=100% band left no room for the new message,
attachments, or skill snapshot (recorded usage excludes the pending
turn), so a near-limit history could overrun the routed model at
request startup instead of compacting. Routed sends now compact within
ROUTED_SEND_COMPACTION_HEADROOM_PERCENT of the routed window — still
far above the workspace threshold, so a cheap skill invocation still
cannot force an unrequested compaction of a fitting history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…odel that fits

Codex review round 3:

- Mid-stream usage updates during a routed turn applied the workspace
  threshold+buffer against the (smaller) routed window, immediately
  forcing the exact surprise compaction the pre-send band declined.
  checkMidStream now takes a force-threshold override and routed turns
  pass the routed-send headroom policy.
- When a class routes UP to a larger-window model, repeated routed
  turns can outgrow the user's model; summarizing on it would just
  context-error again. On-send and mid-stream compaction now run on
  whichever of the user/routed model has the larger usable window
  (normally still the user's model). The deferred follow-up keeps
  pre-routing options and re-routes at dispatch either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review round 4: OpenAI's isConfigured can mean Codex-OAuth-only
credentials, which serve only the OAuth-allowed model set — the class
preflight would pass for an OAuth-ineligible model (e.g. gpt-5.5-pro)
and the direct route would win over a later gateway, only for the
factory to fail with api_key_not_found. canDirectOpenAIServeModel
mirrors the factory's credential selection (OAuth-required models need
stored tokens even with an API key; OAuth-only configs serve only the
allowed set) and the shared servability predicate consults it, so the
editor warning and send-time error stay aligned with what a send would
really do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d model

Codex review round 5:

- ProviderModelFactory.resolveModelRoute now passes the canonical model
  into isProviderAvailableForRouting: a Codex-OAuth-only OpenAI
  credential no longer wins the direct route for a model outside the
  OAuth-allowed set (which createModel would reject with
  api_key_not_found) — a usable gateway later in routePriority wins
  instead, matching the shared canDirectOpenAIServeModel predicate.
  Also realigned that predicate with the factory's actual fallback: an
  API key attempts any model (OAuth-preferred models fall back to the
  key), while tokens-only serves only the allowed set.
- ChatInput's client-side PDF preflight judged the workspace model,
  rejecting routable skill sends before the IPC call ever reached the
  backend's routed-model gate; it now defers to that authoritative
  check when a routable skill invocation is present.
- sendMessage now returns SendMessageAccepted { routedModel } through
  AgentSession → WorkspaceService → router → wire schema, and ChatInput
  attributes successful-send telemetry to the routed model (queued
  sends report none and fall back to the requested model).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… for class rows

Codex review round 6:

- React Compiler conventions ban useCallback for identity
  stabilization: the subscription's fetch now lives inside the effect
  (its natural scope) and the write-failure revert reaches it through a
  ref, so no manual memoization and no exhaustive-deps suppressions.
- ModelsConfiguredPhone pins a Pixel phone matrix variant (mirrored
  with globals.viewport) so CI snapshots the Model Classes rows at the
  narrow width their wrapping layout exists for; verified live at
  375px (label/select wrap, inline no-route warning, no overflow).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…in telemetry

Codex review round 7:

- Class edits persisted AFTER the UI published them: a quick /skill
  invocation would route on the backend's old map while the editor
  claimed the new one. useModelClasses now serializes writes and
  publishes state on the write's ack; rapid edits build on the newest
  pending intent and failures still revert to backend truth.
- The accepted-send payload gains routedThinkingLevel (class thinking
  suffix or re-resolved numeric one-shot) and ChatInput attributes
  message_sent telemetry to it, so small:"haiku+0" invocations stop
  reporting the workspace's ambient thinking level.
- Queued sends still attribute to the requested model: routing resolves
  only at dispatch, and correct queued attribution needs backend-side
  event capture (frontend-only provenance in the payload) — descoped as
  a follow-up; the persisted requestedModel stamp already attributes
  the durable record correctly at dispatch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…king

Codex review round 8:

- Routable skill sends skip the browser PDF preflight (round 5), so a
  queued send whose PDF the routed model rejects died on a bare Err in
  the queue drain — composer already cleared, text and attachment gone.
  Both PDF rejection branches now persist + surface the error through
  preserveRejectedManualSend, like the pricing and routing gates.
- routedThinkingLevel reported the pre-policy value while the stream
  clamps against per-model minimum floors; the floor resolution +
  clamping now live in one shared method used by both the stream build
  and the accepted-send payload.
- messageSent's thinking fallback reads the send's actual options
  (composed one-shot thinking included) rather than the ambient
  workspace setting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review round 9: routedThinkingLevel was populated only when
routing replaced the thinking, so a named one-shot riding through
("/+off /skill") onto a floor-clamped model reported nothing while the
stream ran at the clamped level. The payload now reports whatever
optionsForStream carries — class suffix, re-resolved numeric index, or
a named/ambient level — clamped by the shared per-model floor
enforcement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review round 10:

- Same-session stream retries (retryActiveStream → resumeStream, and
  the post-compaction context-exceeded retry) restarted the stream
  without the routed turn's compaction context, reverting mid-stream
  checks to the workspace threshold against the routed window and
  summarization to the smaller model. The auto-retry resume state now
  carries compactionBaseOptions, resumeStream threads it through, and
  the post-compaction retry reads it from the captured stream context.
- The compact-retry one-shot reparse guard anchored at column zero
  while parseCommand trims leading whitespace; "  /haiku+0 /done" lost
  its override. The guard now trims before checking.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review round 11: the follow-up metadata rebuild passed rawCommand
but dropped commandPrefix, so a recovered composed invocation
("/haiku+0 /done") lost its command badge — UserMessageContent keys its
highlighting on that value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review round 12: a custom openai-compatible provider defined
under the "openai" id is direct-only and authenticates against its own
endpoint (key optional), but canDirectOpenAIServeModel applied built-in
OpenAI credential rules and reported every class model on it
unavailable — rejecting class-bound skills while ordinary sends worked.
The predicate now recognizes the custom-provider shape and defers to
the ordinary isConfigured gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… compaction context

Codex review round 13:

- Rejected EDITS no longer append the edited text as a new tail turn:
  the class-routing and PDF gates skip preserveRejectedManualSend when
  editMessageId is set — preservation exists for dequeued sends whose
  composer already cleared, and the browser restores the edit draft on
  failure.
- Editor rows disable while their write is in flight (per-class pending
  counts from useModelClasses): with state publishing on the write's
  ack, a rapid model→thinking edit would otherwise compose the second
  value from the still-old rendered model and overwrite the first.
- The persisted retrySendOptions embed a durable one-level
  compactionBaseOptions pick for routed turns, and startup recovery
  feeds it back into the resume state — the routed compaction policy
  (90% force bar, larger-window compaction base) now survives a
  relaunch. Rows written by older versions lack the field and fall
  back to today's behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he pricing gate

Codex review round 14:

- deriveStartupAutoRetryRequest never copied the persisted
  retrySendOptions.compactionBaseOptions into the retry request, so the
  round-13 restore always read undefined and a relaunched routed turn
  still reverted to the workspace compaction policy. The builder now
  restores the field.
- The budgeted-goal pricing gate gets the same editMessageId guard as
  the class-routing and PDF gates: a rejected EDIT (reachable here via
  routed skill edits judging the class model) returns bare instead of
  appending the edited text as a new tail turn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@asm
asm force-pushed the skill-model-classes branch from d35a3c7 to b299bc7 Compare August 14, 2026 19:56
…rkspaces

Codex review round 15: child task workspaces prefer creation-time
agent settings for startup retry, which would resume a class-routed
turn on the workspace model while restoring a routed compaction policy
— mismatched in both directions. Retry rows marked with routed
compaction context now use the persisted model/thinking (the class
values the turn actually streamed with); the child's agent identity
precedence is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@asm

asm commented Aug 14, 2026

Copy link
Copy Markdown
Author

@codex review

Round-15 finding addressed: deriveStartupAutoRetryRequest treats a retry row carrying routed compaction context as authoritative for model/thinking even in child task workspaces — the persisted class values win (they're what the turn streamed with), while the child's agent identity keeps its creation-time precedence.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: a89254a376

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant