🤖 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 2 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".
|
@codex review — wave-18 addressed: preDispatchConsentGate inside AIService right before startStream, request-history project-snapshot scanning, durable row-level rejection stamps via HistoryService, consent re-verification on every resumed dispatch, dispatch-time telemetry for compaction-deferred routed skills, and live-subscription fetch retries in the editor hook. Head is 35282db. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 35282db0d9
ℹ️ 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 — wave-19 addressed: the consent gate now executes inside StreamManager's stream-start critical section (nothing awaitable remains before provider dispatch), resumed dispatches carry the same gate with request scanning, rejected-row stamping is fail-closed with in-memory quarantine + recovery re-stamp and includes @file snapshots, accepted-no-stream outcomes drain the queue, and queued skill dispatches emit backend attribution. Head is 5b7babf. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b7babf9e1
ℹ️ 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: 5b7babf9e1
ℹ️ 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.
|
@codex review — wave-20 addressed: per-step consent in prepareStep (fallback/retry recreations included), untrusted historical snapshots filtered from routed requests instead of deterministic rejection, non-retryable consent refusals, preference-independent whole-turn quarantine repair, quarantine coverage for refine and edit summarization, and deferred-dispatch attribution dedup. Head is 3f74763. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f74763617
ℹ️ 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: 3f74763617
ℹ️ 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.
|
@codex review — wave-21 addressed: fallback requests carry the per-step consent gate, consent refusals keep their non-retryable classification through the stream error pipeline, the quarantine repair walks the full epoch, refine's recheck applies the quarantine filter, and queued skill attribution is fully deferred to the backend dispatch capture. Head is 82ba4d1. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 82ba4d1aa0
ℹ️ 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 Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d58879e43f
ℹ️ 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 Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f9b4c8e80
ℹ️ 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: 5f9b4c8e80
ℹ️ 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.
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. 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". |
|
@codex review — rebased onto latest main (145003e: Fable 5.1 promotion + the Effect refactors for config semaphore pipeline, streamManager resource seams, memoryConsolidation, and oRPC streams); conflicts resolved by re-porting this branch's changes onto the refactored structures. No behavioral changes beyond the rebase. |
63c0b5f to
af06984
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af06984ddf
ℹ️ 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: af06984ddf
ℹ️ 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.
Squashed, review-hardened branch (26 Codex waves), rebased onto the Effect
Phase 11 DI layers (ServiceContainer → AppLive/CoreWiringLive/DesktopLive:
the refine and memory-harvest quarantine wiring moved into di/layers), the
project registration lock, the preflight send queue, and the Fable 5.1
promotion (test fixtures reference anthropic:claude-fable-5-1).
Per-skill model routing: skills bind to a class (frontmatter
metadata.model-class, or the skillModelClasses table in config.json which
wins over frontmatter), classes map to models in Settings → Models. Routing
applies to the slash invocation's send only; explicit one-shot models
bypass it, thinking-only one-shots layer on top with model-relative numeric
indices re-resolved against the routed model.
Security: project-scope skills get no class routing (frontmatter or
name-keyed table entry) in untrusted projects — repo-controlled content
must not reroute the transcript to another configured provider, and a repo
shadow must not inherit table consent given to the user's own skill.
Authoritative scope resolves from the on-disk package. Provider-selection
consent is re-verified at every irreversible step through the per-step
provider-call boundary (prepareStep, inherited by fallback/retry
recreations); revoked turns are durably stamped provider-ineligible with
in-memory quarantine plus startup/request-time repair, covering request
assembly, refine, branch summaries, memory harvesting, and compaction
carryover (pending state and RLM tail copies). The provider-boundary gate
also scans post-compaction loaded-skill attachments for project scope:
dropped in untrusted workspaces, gate-arming under trust.
Model-class writes are per-entry atomic in the backend config transaction
(updateModelClass); the Settings editor guards freshness across client
swaps, subscription death, superseding fetches, and transient IPC errors.
Queued and compaction-deferred skill dispatches report { queued: true } and
attribute telemetry at dispatch time via the backend capture, routed or
unbound.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
af06984 to
b6f5a44
Compare
|
@codex review — rebased onto e3c2dba (v0.28.4: Effect Phase 11 DI layers — the refine/memory-harvest quarantine wiring moved into di/layers/core.ts and desktop.ts —, the project registration lock, and the preflight send queue), plus the four follow-ups from the last round (repair-before-commit/clear, rejected-row compaction carryover, attachment-scope consent gate, deferred skill attribution). Replies on each thread. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6f5a44523
ℹ️ 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".
…gate internal retries Codex review follow-ups on b6f5a44: - resumeStream refusals (revoked trust) now stamp the replayed turn's rows and persist the abandon marker WITH the turn's row key (rejectResumedRoutedTurn); retryActiveStream no longer overwrites it with a key-less marker the repair cannot act on. - The pre-snapshot consent recheck rolls back the on-send compaction request row (and its pending follow-up) before returning Err, so startup recovery cannot resume a compaction whose send was reported failed. - The post-compaction context_exceeded retry receives the turn's consent gate (kept on activeStreamContext) like StreamManager's own recreations. - Accepted-but-never-streamed outcomes return { acceptedWithoutStream } and the renderer skips message_sent attribution for them. - Per-step consent refusals pass midStream so the gate does not emit a second stream-error row alongside the stream failure pipeline's. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review — follow-ups for this round's five findings landed in daddef4 (replies on each thread): refused resumes stamp the replayed turn and keep a keyed abandon marker, the on-send compaction recheck rolls back its request row, the post-compaction context_exceeded retry carries the consent gate, accepted-but-never-streamed sends report { acceptedWithoutStream } and skip attribution, and per-step refusals emit once. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: daddef41f0
ℹ️ 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".
| // back: refusing without rolling it back would leave a hidden compaction | ||
| // request that startup recovery resumes — dispatching a prompt whose | ||
| // send was reported failed. Still pre-acceptance, so the set is exact. | ||
| await rollbackPersistedTurnRows(); |
There was a problem hiding this comment.
Handle a failed compaction rollback before returning
When Project Trust is revoked after an on-send compaction row was persisted, rollbackPersistedTurnRows() can return false if deleteMessages() fails and the verification read finds the row still present, but this path ignores that outcome and still reports the send as rejected. Fresh evidence beyond the resolved thread is the helper's explicit failure return: the surviving compaction request retains the prompt as a pending follow-up, so startup recovery can later execute a send the user was told failed. Fail closed by quarantining/invalidating that row or continue acceptance unless its removal is verified.
AGENTS.md reference: AGENTS.md:L110-L112
Useful? React with 👍 / 👎.
| data: { | ||
| ...(result.data?.routedModel != null ? { routedModel: result.data.routedModel } : {}), | ||
| ...(result.data?.routedThinkingLevel != null | ||
| ? { routedThinkingLevel: result.data.routedThinkingLevel } | ||
| : {}), | ||
| ...(result.data?.queued === true ? { queued: true } : {}), | ||
| }, |
There was a problem hiding this comment.
Forward the accepted-without-stream result to the renderer
For a direct routed skill that is durably accepted and then refused by the late consent gate, AgentSession returns { acceptedWithoutStream: true }, but this oRPC adapter rebuilds the success payload without that field. Fresh evidence after the prior telemetry fix is this boundary projection: ChatInput therefore always sees the flag as absent and still records message_sent for a provider request that never occurred. Return the shared backend payload or explicitly preserve this field.
AGENTS.md reference: AGENTS.md:L182-L187
Useful? React with 👍 / 👎.
| await this.repairUnstampedRejectedTurn(); | ||
| // A fresh accepted user send supersedes any persisted startup-abandon | ||
| // classification from previous turns. | ||
| await this.clearStartupAutoRetryAbandon(); |
There was a problem hiding this comment.
Keep the rejection marker when repair still fails
When both rejection stamping and this acceptance-time repair fail, repairUnstampedRejectedTurn() only adds the old turn's rows or surviving partial to the in-memory quarantine, after which this code unconditionally clears the durable abandon marker. Fresh evidence beyond the earlier request-ordering fixes is that the repair returns no success outcome: the current request is protected in memory, but a subsequent app crash loses that quarantine and startup has no marker with which to restamp or delete the rejected content, allowing it into a later provider request. Retain a durable keyed marker until repair is verified successful.
AGENTS.md reference: AGENTS.md:L110-L112
Useful? React with 👍 / 👎.
| if ((options.muxMetadata as MuxMessageMetadata | undefined)?.type === "agent-skill") { | ||
| const dispatchModel = sendResult.data?.routedModel ?? options.model; | ||
| if (dispatchModel != null) { | ||
| await this.captureBackendMessageSent({ |
There was a problem hiding this comment.
Report refused compaction follow-ups as undispatched
When a compaction-deferred project-skill follow-up is durably accepted and then refused by the late trust gate, sendMessage() returns success with { acceptedWithoutStream: true }, but this path ignores that flag, emits dispatch telemetry, and ultimately returns true. TaskService.handleStreamEnd() treats that compaction-completion decision as proof that a continuation stream is running and returns early; because no replacement stream will ever end, a child task can remain running and its parent can wait indefinitely. Fresh evidence after the accepted-without-stream fix is that this internal dispatch path reads only the routed model fields from the new payload; return an undispatched outcome and skip attribution when the flag is set.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: daddef41f0
ℹ️ 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.
| : undefined; | ||
| await this.persistStartupAutoRetryAbandon("pre_stream_rejected", resumedUserMessage?.id); |
There was a problem hiding this comment.
Security: Fail closed when the resumed-turn key cannot be read
Requires a routed project-skill retry, trust revoked before resume, and a transient history-tail read failure. Here failed getLastMessages() makes resumedUserMessage undefined, yet a keyless pre_stream_rejected marker is persisted; repairUnstampedRejectedTurn() then returns without stamping or quarantining rows. The next accepted manual send repeats that no-op, clears the marker, and can send the original project snapshot. Fresh evidence beyond the resolved resumed-turn thread is this failure branch. Refuse completion unless a row key is recovered, or quarantine the active retry rows through another durable mechanism.
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