🤖 feat: route skills to model classes (Settings-managed large/medium/small) - #3849
🤖 feat: route skills to model classes (Settings-managed large/medium/small)#3849asm wants to merge 27 commits into
Conversation
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@codex review All three findings addressed in b1b0bf8:
|
There was a problem hiding this comment.
💡 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".
|
@codex review Round-2 finding addressed in 6c4903d: routed sends now compact within |
There was a problem hiding this comment.
💡 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".
|
@codex review Both round-3 findings addressed in 297b210:
|
There was a problem hiding this comment.
💡 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".
|
@codex review Round-4 finding addressed in 50b68ee: added |
There was a problem hiding this comment.
💡 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".
|
@codex review All three round-5 findings addressed in 6452b8a:
|
There was a problem hiding this comment.
💡 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".
|
@codex review Both round-6 findings addressed in 47afc04:
|
There was a problem hiding this comment.
💡 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".
|
@codex review Round-7 findings in 44facc0:
|
There was a problem hiding this comment.
💡 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".
|
@codex review All three round-8 findings addressed in 79fb8e0:
|
There was a problem hiding this comment.
💡 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".
|
@codex review Round-9 finding addressed in 6284377: |
There was a problem hiding this comment.
💡 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".
6284377 to
3d6ffbd
Compare
|
@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
|
There was a problem hiding this comment.
💡 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".
|
@codex review Round-11 finding addressed: the compact-and-retry metadata rebuild now carries |
There was a problem hiding this comment.
💡 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".
|
@codex review Round-12 finding addressed: |
There was a problem hiding this comment.
💡 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".
…l paths) - useModelClasses: a client swap now marks the hook unloaded and invalidates in-flight fetches, so a reconnect window can't accept a full-map write built from another process's stale map. - docs: point model-class config at canonical ~/.xum/config.json and describe the real compaction base selection (larger-window model, Compact-agent settings still win); regenerated built-in skill content. - rebase reconciliation: widen the new task/workspace seam's sendMessage to the SendMessageAccepted payload; return a real TurnStreamHandle from the routing test's streamMessage stub (turn-engine refactor). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f5dd3e3 to
9840eb0
Compare
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (3)
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Security review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9840eb0d58
ℹ️ 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".
…, validate routed-retry context Codex round 2 on the rebased branch: - useModelClasses ties write acknowledgements (and failure refetches) to the client generation that issued them, so an old client's late ack can no longer publish over — or invalidate the in-flight fetch of — the replacement client after a reconnect. - Codex OAuth speaks only the Responses endpoint: with openai pinned to wireFormat chatCompletions and no real API key, both the shared canDirectOpenAIServeModel preflight and the factory's route-selection gate now refuse the direct route (createModel rejects it with api_key_not_found), letting a configured gateway win instead. - Persisted compactionBaseOptions from chat.jsonl is shape-validated (object with a non-empty model, nested field stripped) before it can mark a row routed or ride into the resume request; malformed values fall back to today's non-routed behavior. Regression test covers a corrupted boolean in a child task workspace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Security review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e835591c2
ℹ️ 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".
…efore the pricing preflight Codex round 3: - useModelClasses resets the pending intent map, the write chain, and pending-row counts on a client swap (completions from the old client skip their bookkeeping via the generation guard), so a new edit can neither compose from dead intent nor queue behind a request that may never settle. - The persisted routed-retry sanitizer applies the startup-model bar (normalizeSelectedModel + isValidModelFormat) instead of accepting any non-empty string, and forwards the normalized id. - WorkspaceService.sendMessage defers its pricing preflight for skill sends: class routing resolves inside AgentSession, whose dispatch-time gate re-asserts pricing against the model that actually streams — the ambient-model preflight would reject a skill bound to a priced class on an unpriced workspace model. Deferral can't corrupt stored settings (composer skill sends re-persist the already-selected model; one-shots skip persistence). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Security review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 836dd19681
ℹ️ 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".
… on gate rejections Codex round 4: - The deferred pricing preflight for skill sends also deferred nothing for persistence: an unbound skill (or skipSkillModelRouting caller) could get its unpriced ambient model persisted before AgentSession rejected it. Skill sends now persist AI settings from the session's onAccepted callback — fired only after every dispatch-time gate passed, and carried through queued dispatch by MessageQueue. Test asserts the ordering. - The class-routing and PDF rejection branches now thread internal.enqueuedAtMs into the preserved row and goal safety, matching the pricing branch, so a skill queued before a goal activation cannot wrongly pause the fresh goal (nor be misclassified after restart). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e16c845436
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: e16c845436
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
…ontext, safe persistence Codex round 5: - Security: a project-scope skill's model-class frontmatter only applies in trusted projects (Project Trust is the existing consent boundary for repo-controlled configuration) — an attacker-controlled repository could otherwise silently reroute the transcript to any provider the user bound to a class. Global/built-in skills and the user's own skillModelClasses table are unaffected. Tested both directions; documented. - Persisted compactionBaseOptions is now schema-parsed with SendMessageOptionsSchema (unknown keys stripped, malformed siblings like providerOptions: false reject the whole context) instead of validating only the model, so corrupted values can't reach provider request construction via buildAutoCompactionRequest. - The acceptance-deferred AI-settings persist is wrapped: a persistence exception must not propagate through onAccepted and turn an accepted send (durable user row) into a partial failure. - ModelClassesEditor candidates dedupe via the metadata-aware normalizeFallbackModelKey, so a cross-typed Coder selection (coder:openai/x on an anthropic instance) keeps its explicit gateway route instead of persisting a direct-provider id. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Security review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 121cb4b98e
ℹ️ 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".
…es, 390px story viewport Codex round 6: - The persisted routed-context sanitizer now filters to the exact key set pickStartupRetrySendOptions persists (exported as STARTUP_RETRY_DURABLE_SEND_OPTION_KEYS) BEFORE schema-parsing, and narrows muxMetadata to workspace-turn correlation like the durable pick does — a schema-valid but non-durable field (editMessageId) can no longer ride a corrupted row into buildAutoCompactionRequest and send the restored compaction request down the edit/truncation path. - Model-class candidates dedupe by the metadata-aware key but keep each first-seen RAW selection as the value, so explicit gateway selections (openrouter:openai/x, default-typed coder:anthropic/x) persist their gateway identity instead of collapsing to direct-provider ids. - New pixelPhone (390px) Storybook viewport mirrors the Pixel matrix's named phone width; the pinned ModelsConfiguredPhone story uses it so the local view renders at the exact width CI snapshots. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: a7a2e8281d
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a7a2e8281d
ℹ️ 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".
| const key = normalizeFallbackModelKey(model, providersConfig); | ||
| if (!candidatesByKey.has(key)) { | ||
| candidatesByKey.set(key, model); |
There was a problem hiding this comment.
Keep direct and gateway routes as separate candidates
When both a direct model and its explicit gateway form are available, normalizeFallbackModelKey() maps them to the same key, so this keeps only whichever appears first. For example, Settings supplies custom models before built-ins, so openrouter:openai/gpt-5 can remove openai:gpt-5 from every class picker; the user can no longer select the direct route even though the two values dispatch differently. Deduplicate exact selection identities rather than fallback-chain keys.
Useful? React with 👍 / 👎.
| const persisted = await this.preserveRejectedManualSend( | ||
| message, | ||
| options, | ||
| routingError, | ||
| internal?.enqueuedAtMs |
There was a problem hiding this comment.
Preserve the typed command when routing rejects a queued skill
When a queued skill is rejected here because its class binding is invalid or unavailable, message is the model-facing text such as Using skill done: ...; the original /done ... command exists only in options.muxMetadata. preserveRejectedManualSend() creates a normal row without that metadata, so the only recovered transcript row loses the user's typed command and skill badge and instead exposes the rewritten provider prompt. Preserve the raw command display metadata on this rejection path.
Useful? React with 👍 / 👎.
Codex round 7 (security): a project skill shadows a global/built-in name at collision time, so a name-keyed skillModelClasses entry — consent the user gave for the skill they knew — could route an untrusted repo's shadow to the bound provider. Scope now resolves from the package (never from the client-supplied invocation metadata) before ANY binding applies: in an untrusted project a project-scope skill gets no class routing at all, frontmatter or table. The trusted table-binding fast path keeps skipping the SKILL.md read. Flipped the untrusted-table test to assert shadow protection and documented the rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: 6a11d23f43
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
| } | ||
| const projectTrusted = (() => { | ||
| try { | ||
| return isWorkspaceProjectTrusted(this.config, metadataResult.data); |
There was a problem hiding this comment.
Security: Require trust for scratch project-skill routing
Condition: a scratch workdir contains an attacker-controlled repository, a class maps to another configured provider, and the user invokes the repository's skill. This check calls isWorkspaceProjectTrusted, which returns true for every scratch workspace, so project frontmatter bypasses the untrusted-project guard. Scratch folders are writable and terminal-enabled, and discovery scans their .xum/skills; the hidden model-class then replaces the stream model and sends active history to that provider. Fresh evidence beyond the resolved project-trust finding is this unconditional scratch exception. Require explicit trust for project skills found in scratch workdirs.
Useful? React with 👍 / 👎.
Summary
Skills can now be routed to user-defined model size classes so mechanical skills (wrap-up chores, formatting passes, routine repo tasks) 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 frontmattermetadata: model-class: smallor a localskillModelClassesconfig 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 /deep-review), 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.model) and extends it to compose with skill slash invocations.ai.modelparsed but not consulted); this PR takes the same position for skills — a declared model preference should be honored — while keeping it strictly opt-in.metadatamap, which other harnesses ignore. Frontmatter bindings to a class the user never defined are deliberately inert, so skills shippingmetadata: model-classcan 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
modelClassesandskillModelClassesrecords (schema, load normalization,saveConfigwhitelist,config.updateModelClassesroute). 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.src/common/utils/ai/skillModelClasses.ts): binding resolution as a discriminated union (unbound/unknown-class/invalid-value/resolved), plusisModelServableWithProvidersConfig(modelAvailability.ts) wrapping the routing layer'sisModelAvailablewith the same exported provider/gateway predicatesuseRoutingconsumes — 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.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 dedicatedskipSkillModelRoutingsend option (set by explicit one-shot composition and compaction retries) rather than overloadingskipAiSettingsPersistence. 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.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.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.parseCommandWithSkillInvocationcomposes a leading one-shot with a skill invocation by re-runningparseCommandon 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 —+0means 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 (withskipAiSettingsPersistence, so a re-dispatch never persists one-shot values as new workspace defaults), andprepareCompactionMessagekeeps carried one-shot fields from being clobbered by ambient stored options.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):
skipAiSettingsPersistence), and process relaunch (durablecompactionBaseOptionsinretrySendOptions, honored even in child task workspaces).ProviderModelFactory.resolveModelRouteboth apply model-aware OpenAI credential rules (Codex-OAuth-only serves the OAuth set; API keys attempt anything; custom openai-compatible providers shadowing theopenaiid are exempt).routedModel+ post-floorroutedThinkingLevel; persisted metadata re-stampsrequestedModel. Queued-send event attribution is documented as a follow-up (needs backend-side event capture).Validation
saveConfigwhitelist (including preservation of unknown classes), end-to-end AgentSession routing and error paths via the session harness (gate ordering,skipSkillModelRoutingexemption, thinking-only bindings, compaction follow-up model), composition parser cases, and editor UI behavior (clear preserves custom classes; load gating; warning states).bun test srcfailure set is identical tomain's on the same machine (pre-existing env-sensitive tests only).ModelsSectionstories 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 carryingagent-skillmetadata withoutskipSkillModelRoutingare considered, and workspaces with nomodelClasses/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