Skip to content

fix(custom-model): context limits, Codex model override, context discovery and picker filter - #752

Merged
Ark0N merged 13 commits into
Ark0N:masterfrom
opticon454:feature/custom-endpoint-protocol
Nov 4, 2026
Merged

Ark0N merged 13 commits into
Ark0N:masterfrom
opticon454:feature/custom-endpoint-protocol

Conversation

@opticon454

@opticon454 opticon454 commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #749, #750 and #754.

Split from the original version of this PR at the maintainer's request, so these fixes no longer wait on two separate decisions. The Anthropic endpoint type and the DeepSeek rewrite now have their own PRs (linked below once opened).

Changes

  • Context limits (OpenCode custom endpoints omit known context limits and overflow local servers #749): OpenCode config carries limit.context (the full window) and limit.output from the discovered context length.
  • Codex model override (Switching Codex to a custom endpoint retains the native launch model #750): the selected model is passed as the launch override (a model id may now contain : after a leading alphanumeric, so qwen3:32b launches), and model_context_window is set.
  • Context discovery (Custom endpoints behind a gateway never get a context length, so Claude assumes 200k #754): discovery reads context_length, else context_window, from each /v1/models entry, so gateways such as Bifrost in front of llama-swap get real sizes. The llama-swap /running launch command and /props still win for a loaded model. A listed figure is a per-slot window, so it is stored with one slot and can no longer pair with a slot count from an earlier loaded round. A per-endpoint defaultContextLength covers models whose size cannot be discovered, and discovery never overwrites it.
  • Boot recovery: the default context length applies when sessions are rebuilt after a restart, through one shared helper used by both routes and recovery.
  • Model picker: a search box, standard spacing, "No models match" only when a query hides every row, and zh-CN strings for both.
  • Docs: docs/api-reference.md, docs/custom-model-endpoints.md, docs/architecture-invariants.md.

Review items from the first round

Items 1, 2, 5 (core docs), 6 and the smaller ones are in. Items 3 and 4 (DeepSeek) moved with the DeepSeek rewrite.

Verification

  • npm run typecheck, lint and format:check are clean.
  • Full npm test on this branch: 666 files passed, 13,530 tests passed, 12 skipped, 0 failed.
  • New tests cover boot recovery with the endpoint default, the two-round discovery case (a loaded 262144 context with 4 slots, then an unloaded model listing 65536), and the Codex colon id.

@opticon454
opticon454 marked this pull request as ready for review November 1, 2026 11:05
@Ark0N

Ark0N commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Thank you for this, Devvyn, and for the careful issue write-ups behind it. This PR passes known context limits to OpenCode, Codex and DeepSeek, makes Codex launch on the selected custom model, reads context sizes from /v1/models with a per-endpoint default, adds an Anthropic endpoint type and adds a search box to the model picker. CI is green and the core fixes are sound. A few things need to change before it can merge:

1. Boot recovery skips defaultContextLength (src/web/server.ts:1473). _rebuildCustomModelEnv() is the third place that reads the context length, and it still passes endpoint.modelContextLengths?.[saved.modelId] alone. Recovery rewrites the config files, so after a Codeman restart a Codex or DeepSeek session that relied on the endpoint default loses its limit at the next respawn. Please add the same ?? endpoint.defaultContextLength fallback (a small shared helper used by all three sites keeps them from drifting) and a recovery test.

2. A listed figure overwrites a /running figure on a later sweep (src/web/routes/custom-model-routes.ts:363, merged at :735). Within one round the precedence is right, but when the model is unloaded at the next background sweep the listed value replaces the stored launch-command value and the stored slot count stays. llama-swap documents its listed context_length as the per-slot window, so the floor check divides it again. Repro: round 1 loaded with --ctx-size 262144 --parallel 4 stores 262144 and 4 slots; round 2 unloaded, listing 65536, stores 65536 and 4 slots, so Claude gets a false context warning (16384 per slot). Recording slotCounts[id] = 1 whenever the listed figure is the one taken (the same rule the /props branch already applies) fixes it. Please add a two-round test.

3. DeepSeek transcripts end up in a directory that gets deleted (src/config/cli-registry/stock.ts:1558). With DSH_HOME pointed at the per-session config dir, dsh writes its session logs to <config dir>/sessions (dsh-base's session-persistence-jsonl root is dshHomePath('sessions')), and removeConfigDir deletes that directory on clear and when the tab closes. The user's own settings.yaml and home-level patch also stop applying. Please link sessions (and any other state that needs to survive) back to the real dsh home, the way linkSharedProjectsDir() does for Claude's projects, or pass the generated patch with --patch instead of redirecting DSH_HOME.

4. The DSH_HOME special case belongs in the registry (src/custom-model-injection-apply.ts:49). CLAUDE.md asks that no code outside stock.ts branch on a CLI, and dirEnvVar === 'DSH_HOME' does that by env var name. Please express it as a field on the configDir recipe (for example, which entries to link from the CLI's real home) and consume it generically. In the same block: existsSync returns false for a dangling link, so a re-apply (recovery runs one on every restart) throws EEXIST; use lstat and a try/catch like linkSharedProjectsDir(), and resolve the home from the session's own DSH_HOME before the server's.

5. Docs. docs/api-reference.md:1207 needs endpointType and defaultContextLength on the POST/PUT body and the new discovery source; docs/architecture-invariants.md:630 and :637 still describe DeepSeek as an env recipe with appendV1Suffix; docs/custom-model-endpoints.md (lines 71, 302 and 535) still describes the old discovery and DeepSeek behaviour; the appendV1Suffix comment in src/config/cli-registry/types.ts:858 still names DeepSeek.

6. Codex and : in model ids (src/config/cli-registry/stock.ts:848). Forcing the launch model runs the id through codex's model pattern, which has no :, so an Ollama-style id such as qwen3:32b now gets a 400 where it used to launch when no native Codex model was set. Please admit : for this case (with a leading alphanumeric, as model-plain does) or skip the forced launch param when the id cannot ride it.

Smaller things, fine to fold in: opencodeModelLimit (src/custom-model-injection.ts:234) subtracts the output reservation before writing limit.context, but OpenCode treats limit.context as the full window and reserves for the reply itself, so passing the real total is enough; Search models and No models match are missing from the zh-CN table; and test/Deepseek-CustomEndpoint.test.ts would match the rest of test/ as deepseek-custom-endpoint.test.ts.

On scope: CONTRIBUTING asks for one change per PR. The Anthropic endpoint type and the DeepSeek rewrite have no issue behind them, and the endpoint type has an open design question: withAnthropicRootSuffix/withAnthropicV1Suffix hardcode a /anthropic path segment, which is Bifrost's routing layout rather than part of the Anthropic protocol, so the alternative is to treat the saved base URL as the Anthropic base and only normalise the /v1. That choice is still open on my side. Moving the endpoint type and the DeepSeek rewrite into their own PRs keeps the #749, #750 and #754 fixes from waiting on that decision.

Once those are addressed, this is ready for another review.

…d add model picker filter

- opencode: write limit.context/output from the discovered context length (Ark0N#749)
- codex: override the native launch model when a custom endpoint is selected (Ark0N#750), and set model_context_window
- model picker: add a search/filter box with a no-matches line
… injection

- share one context-length resolution (discovered figure, endpoint default fallback) across the routes and boot recovery, with a recovery test
- record listed per-slot context figures with a slot count of 1 and force the slot-count fallback once per rediscovery
- open up the codex model launch token for qwen3-style ids (model-plain-colon), pinned by a spawn-golden case
- pass the full discovered window to opencode as limit.context
- translate the picker search strings and document the discovery precedence
@opticon454
opticon454 force-pushed the feature/custom-endpoint-protocol branch from 36f620c to 1b8ac40 Compare November 2, 2026 03:58
@opticon454 opticon454 changed the title fix(custom-model): context limits, Codex model override, DeepSeek patch and model picker filter fix(custom-model): context limits, Codex model override, context discovery and picker filter Nov 2, 2026
@opticon454

Copy link
Copy Markdown
Contributor Author

Pushed 1b8ac40, and split the PR as asked.

Each branch is based on master and stands alone. #752 and #755 touch the same recipe lines in stock.ts, types.ts and i18n.js, so whichever merges second needs a rebase; tell me which order you prefer. Full npm test on each: core 13,530 passed, DeepSeek 13,506 passed, Anthropic 13,523 passed, 0 failed.

@Ark0N

Ark0N commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Thank you, Devvyn, for the quick turnaround and for splitting this so cleanly. This PR now carries the core of the work: discovered context limits for OpenCode and Codex, Codex launching on the selected model (colon ids included), context discovery from /v1/models with a per-endpoint default, one shared helper so boot recovery keeps that default, and the search box in the model picker. Every round-one item that stayed here is in, each with a test, and typecheck, lint, format, the frontend syntax check and the full test suite (666 files, 13,530 tests) are green on my side.

A few small things remain:

  1. docs/api-reference.md:1228 and :1239 contradict the code. The paragraph still opens with "only for a model llama-swap's own response already reports loaded", and the new sentence says a server with no status field "gets no context/size enrichment at all". discoverModels (src/web/routes/custom-model-routes.ts:359) reads context_length/context_window and the size for every entry before the status check, which is the whole point of Custom endpoints behind a gateway never get a context length, so Claude assumes 200k #754. The wording in docs/custom-model-endpoints.md:88-91 ("no launch-command//props enrichment, only whatever its listings state") is the one to copy.
  2. The Codex context lines have no test (src/custom-model-injection.ts:245). model_context_window and model_auto_compact_token_limit are half of the Switching Codex to a custom endpoint retains the native launch model #750 fix. A quick probe shows they render as top-level TOML keys as intended, but nothing pins that. Please add a codex-toml case to test/custom-model-injection.test.ts that parses the file and checks both keys, plus the undefined/NaN case writing neither, like the OpenCode tests you added.
  3. An open question, not a change request: Codex and OpenCode receive the whole KV pool (src/web/routes/session-routes.ts:1547 and :4455, src/web/server.ts:1473). With your own fixture, --ctx-size 262144 --parallel 4, a round that sees the model loaded gives Codex model_context_window = 262144 (compacting at 196608), and after a sweep finds it unloaded the listed per-slot figure gives the same model 65536, so the window these two CLIs get changes fourfold with the timing of the last sweep. slotCount only feeds the Claude floor check, and Claude's CLAUDE_CODE_MAX_CONTEXT_TOKENS already receives the whole pool. Whether Codex and OpenCode should receive the per-slot window instead, and whether in this PR or later, is still open on my side.
  4. Codex now refuses some ids it used to launch (src/config/cli-registry/stock.ts:847). With launchModel forced, an id outside model-plain-colon is a 400, for example LM Studio's org/model@q4_k_m, which config.toml alone carried before. Please note the accepted id shape in docs/custom-model-endpoints.md next to the Codex notes, so the 400 does not come as a surprise.
  5. Docs that still describe the old behaviour: src/config/cli-registry/types.ts:816 ("codex's top-level model key"); docs/architecture-invariants.md:633 and docs/custom-model-endpoints.md:293-296 (only pi, omp and grok get --model; Codex does too now); docs/custom-model-endpoints.md:455-461 (the floor warning now also fires on defaultContextLength, so "never discovered skips the check" no longer holds); :535 (Codex now does get a context-window override); and the user manual, docs/wiki/Custom-Model-Endpoints.md:29-37 and :119 (listed figures, the Default context length field, OpenCode and Codex receiving the window, the picker search). model_auto_compact_token_limit at 75% is not mentioned anywhere yet.

Smaller, fine to fold in:

  • Say in the defaultContextLength doc comment (src/custom-model-hosts.ts:94) and the editor hint (src/web/public/index.html:2803) whether the figure is the server's total or one slot's window, since the floor check divides it by any stored slot count.
  • Three comments still describe the old state: src/web/routes/custom-model-routes.ts:168 ("loaded-models-only basis"), src/web/routes/session-routes.ts:1614 and src/session.ts:2726 (only pi/omp/grok).

None of this changes the direction. Items 1, 2, 4 and 5 are the changes to make; the docs ones are small enough that they can also be applied at merge time. Beyond those, what remains is the question in item 3 and which of this PR and #755 merges first, both still open.

@Ark0N

Ark0N commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Thank you, Devvyn, for the follow-up push. This PR passes discovered context limits to OpenCode and Codex, launches Codex on the selected custom model (colon ids included), reads context sizes from /v1/models with a per-endpoint default that boot recovery keeps, and adds a search box to the model picker.

The latest commit (5211f38) adds the "In use by a session" promotion, but none of the round-two items made it in. What still needs to change:

  1. The Codex context lines still have no test (src/custom-model-injection.ts:245-250). Please add a codex-toml case to test/custom-model-injection.test.ts that parses the rendered config.toml (smol-toml is already a dependency) and checks model_context_window and model_auto_compact_token_limit as top-level keys (16384 gives 16384 and 12288), plus undefined and NaN writing neither. The rendering itself is right: a probe at this head gives exactly those values. This only needs the pin.

  2. Please move the "In use by a session" work to its own PR. Commit 5211f38 (_getCustomModelInUseBySession at src/web/public/session-ui.js:1071-1083, the #customModelPickUnknown line, its CSS and i18n strings, the docs section and .changeset/custom-model-picker-in-use.md) is a new picker feature that the PR description does not mention, and it landed after round two. CONTRIBUTING asks for one change per PR, which is why this PR was split before, and taking it out keeps the three issue fixes from waiting on a review of the new feature. One thing to carry over with it: at session-ui.js:1114-1136 a remembered "last used" id that is no longer in host.models keeps promoted truthy, so no row is tagged and the "Nothing is marked" line stays hidden. Deriving it from promoted && models.includes(promoted) fixes that.

  3. This PR needs a changeset of its own. The current one describes only the in-use promotion, so the release notes would say nothing about what this PR ships: the OpenCode and Codex context limits (OpenCode custom endpoints omit known context limits and overflow local servers #749), Codex launching on the selected model (Switching Codex to a custom endpoint retains the native launch model #750), discovery from /v1/models plus the Default context length field (Custom endpoints behind a gateway never get a context length, so Claude assumes 200k #754), and the picker search box. (The current file is also mode 100755; the other .changeset files are 100644.)

  4. One sentence on the Codex id shape in docs/custom-model-endpoints.md next to the Codex notes. With launchModel forced (src/config/cli-registry/stock.ts:847), an id outside the model-plain-colon pattern (a letter or digit first, then letters, digits and ._-/:) is now a 400, for example LM Studio's org/model@q4_k_m, which config.toml alone carried before.

Docs that can also be applied at merge time if they are not in your next push (round-two items 1 and 5):

  • docs/api-reference.md:1228-1229 and :1237-1239 still say context/size enrichment happens only for loaded models and that a server with no status field gets none; discoverModels reads the listed figures for every entry. The wording at docs/custom-model-endpoints.md:84-85 is the one to copy.
  • src/config/cli-registry/types.ts:815-816 (Codex's top-level model key), docs/architecture-invariants.md:633 and docs/custom-model-endpoints.md:322-326 (only pi, omp and grok get --model), docs/custom-model-endpoints.md:489-491 (the floor warning now also fires on defaultContextLength), :562-568 (Codex now does get a window override; model_auto_compact_token_limit at 75% is documented nowhere), and the user manual, docs/wiki/Custom-Model-Endpoints.md:29-37 and :119.
  • Comments: src/web/routes/custom-model-routes.ts:168, src/web/routes/session-routes.ts:1613-1614, src/session.ts:2726, and src/custom-model-hosts.ts:94-101 plus the editor hint at src/web/public/index.html:2808 (say whether defaultContextLength is the server total or one slot's window, since the floor check divides it by any stored slot count).

Fine to fold in: search.focus() at src/web/public/session-ui.js:1175 raises the on-screen keyboard over the list on phones every time the picker opens. src/web/public/readmymind-ui.js:161 skips the focus when MobileDetection.isTouchDevice() is true, for the same reason.

Still open on my side, not a change request: whether Codex and OpenCode should receive the per-slot window rather than the whole KV pool (round-two item 3), and which of this PR and #755 merges first.

Once the Codex test, the id-shape note, the changeset and the split are in, what remains before merge is those two open questions.

@opticon454

Copy link
Copy Markdown
Contributor Author

Pushed 39d211f, thanks.

  1. docs/api-reference.md now matches the code: listed context_length/context_window and the size are read for every entry before the status check; only the launch-command and /props enrichment need a model reported as loaded (worded as in docs/custom-model-endpoints.md).
  2. New codex-toml tests parse the generated TOML and check model_context_window and model_auto_compact_token_limit (75%, floored) as top-level keys, and that an undefined, NaN or 0 length writes neither.
  3. The accepted Codex launch-id shape is documented next to the Codex notes, with the org/model@q4_k_m example and the 400.
  4. The stale docs and comments are updated: Codex's --model and context-window override, the floor warning also firing on defaultContextLength, the user manual (listed figures, Default context length, OpenCode and Codex receiving the window, the picker search, the 75% compaction), and the three old comments.
  • defaultContextLength's doc comment and the editor hint now say it is the model's whole window, which the Claude floor check divides by any stored slot count.

Item 3 (whole pool or one slot for Codex and OpenCode) I left alone as the open question; my own view is that the per-slot window is the safer figure for both, since it does not change with the sweep timing, but that is your call and I will do it here or later as you prefer.

I rebased onto 5211f38 (the picker change that landed on this branch). Full npm test: 13,540 passed, 0 failed.

@Ark0N

Ark0N commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Thank you, Devvyn, for the quick follow-up push. This PR passes discovered context limits to OpenCode and Codex, launches Codex on the selected custom model (colon ids included), reads context sizes from /v1/models with a per-endpoint default that boot recovery keeps, and adds a search box to the model picker.

Every round-two item is in at this head: the codex-toml test, the id-shape note, the api-reference wording and the stale docs and comments. Typecheck, lint, format, the frontend syntax check, the catalogue check and the touched test files are green here. Your push and my round-three comment crossed by a few minutes, so the round-three items are still open. Here is the full list in one place:

  1. Please move the "In use by a session" work to its own PR. That is commit 5211f38: _getCustomModelInUseBySession at src/web/public/session-ui.js:1071, the #customModelPickUnknown line at src/web/public/index.html:1048, its CSS and zh-CN strings, the docs/custom-model-endpoints.md section and .changeset/custom-model-picker-in-use.md. CONTRIBUTING asks for one change per PR, and this keeps the three issue fixes from waiting on a review of a new feature. The search box stays here. Two fixes travel with the split:

    • session-ui.js:1136: a remembered "last used" id that is no longer in host.models keeps promoted truthy, so no row is tagged and the "Nothing is marked" line stays hidden. Deriving it from promoted && models.includes(promoted) fixes that.
    • The changeset file is mode 100755; the other .changeset files are 100644.
  2. This PR needs a changeset of its own. Once the current one moves with the split, nothing here describes what ships: the OpenCode and Codex context limits (OpenCode custom endpoints omit known context limits and overflow local servers #749), Codex launching on the selected model (Switching Codex to a custom endpoint retains the native launch model #750), discovery from /v1/models plus the Default context length field (Custom endpoints behind a gateway never get a context length, so Claude assumes 200k #754), and the picker search box. A patch changeset covering those is enough.

  3. A refused Codex model id deletes the session's live config directory (src/web/routes/session-routes.ts:1635). POST /api/sessions/:id/custom-model writes the new config.toml into customModelConfigDir(session.id), which is the same directory a Codex session already on an endpoint uses as CODEX_HOME. Only after that does it check the forced launch id, and on a refusal it removes that directory.

    • Before this PR Codex had no launchModel, so the check never ran for it. With launchModel: '{modelId}' (src/config/cli-registry/stock.ts:847), applying org/model@q4_k_m to a session running on qwen3:32b now returns 400 and wipes its config.toml and everything Codex wrote there, while the session stays pinned to qwen3:32b. I reproduced it with a route test.
    • Please run the pattern check before any write: buildCustomModelInjection() is pure and already returns launchModel, so matchesPattern(modelSpec.pattern, injection.launchModel) can run before applyCustomModelInjection(). Then add a test: two applies on one codex session, the second refused, the directory and its contents still there.
    • The quick-start path is fine as it is, since it removes a directory it minted for a session that does not exist yet.
  4. Skip search.focus() on touch devices (src/web/public/session-ui.js:1175). It raises the on-screen keyboard over the list every time the picker opens on a phone. src/web/public/readmymind-ui.js:161 guards the same thing with MobileDetection.isTouchDevice().

Smaller, fine to fold in (otherwise I will apply them at merge time):

  • docs/api-reference.md:1218: say that PUT without defaultContextLength clears it, since the same paragraph says an omitted apiKey is kept.
  • docs/wiki/Custom-Model-Endpoints.md:36-38: the sentence reads as if the default context length beats a figure learned in an earlier cycle. The code is modelContextLengths[id] ?? defaultContextLength, so any stored figure wins.
  • src/web/routes/custom-model-routes.ts:195: the comment says MAX_DISCOVERED_CONTEXT_TOKENS is shared with schemas.ts, but the schema (:2378, :2385) repeats the literal. One exported constant would make that true.
  • test/routes/quick-start-custom-model.test.ts:114: one expect(session.getCustomModelForPersist()?.launchModel).toBe('qwen3') would pin Switching Codex to a custom endpoint retains the native launch model #750 on the one-shot path, like the pi/grok/omp tests beside it.

For a later PR, not this one: the quick-start path writes the launch model into the stored codexConfig.model (session-routes.ts:4597), so a later API clear relaunches native Codex with the custom id. pi, grok and omp already behave this way at master.

Two questions are still open on my side, not change requests:

Once items 1 to 4 are in, what remains before merge is those two questions.

…rite

Round-four review item 3: the swap route wrote the new config.toml into the very dir a live codex session uses as CODEX_HOME before checking the pattern, and its refusal then deleted that dir. buildCustomModelInjection is pure, so the check runs on its result and a refused id never touches the filesystem. Pinned by a two-apply codex route test.
Round-four review item 4: focusing the search input on every picker open raises the OS keyboard over the list on phones; guard it the way readmymind-ui.js does.
…r routes and schema

Round-four review smaller item: the discovery comment claimed a shared ceiling while schemas.ts restated the literal twice; the constant now lives in schemas.ts (routes already import from it, no cycle) and both bounds reference it.
…ed figure wins

Round-four review smaller items: api-reference now states the opposite-of-apiKey behaviour for an omitted defaultContextLength, and the wiki sentence follows the code, modelContextLengths[id] ?? defaultContextLength: any stored figure beats the default.
Round-four review smaller item: one getCustomModelForPersist().launchModel expectation, like the pi/grok/omp tests beside it, pins Ark0N#750 on the one-shot path.
@opticon454
opticon454 force-pushed the feature/custom-endpoint-protocol branch from 39d211f to 884e67d Compare November 2, 2026 09:18
@opticon454

Copy link
Copy Markdown
Contributor Author

Pushed 884e67d, thanks. Your round-three and my push did cross; here it is in one place.

  1. The "In use by a session" work is out of this PR (it was 5211f38): its own branch and PR, stacked on this one. The two fixes travel with it (a remembered id missing from host.models no longer counts as promoted, and the changeset is mode 100644). The search box stays here.
  2. This PR has its own patch changeset covering OpenCode custom endpoints omit known context limits and overflow local servers #749, Switching Codex to a custom endpoint retains the native launch model #750, Custom endpoints behind a gateway never get a context length, so Claude assumes 200k #754 and the picker search.
  3. The swap route now checks the forced launch id before any write: buildCustomModelInjection() runs first and matchesPattern(modelSpec.pattern, injection.launchModel) before applyCustomModelInjection(), so a refused id never touches customModelConfigDir(session.id). New test: two applies on one codex session, the second refused (org/model@q4_k_m), and the directory and its contents are still there. The quick-start path is unchanged.
  4. search.focus() is skipped on touch devices (MobileDetection.isTouchDevice()).

Smaller ones: docs/api-reference.md says a PUT without defaultContextLength clears it, the wiki says any stored per-model figure wins over the default, MAX_DISCOVERED_CONTEXT_TOKENS is one exported constant used by the routes and the schema, and the quick-start test pins launchModel to qwen3. I left the later-PR item (codexConfig.model on quick-start) alone.

The two open questions stay with you; for the first, my view is unchanged (the per-slot window is the safer figure for Codex and OpenCode). Full npm test: 13,534 passed, 0 failed.

@Ark0N

Ark0N commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Thank you, Devvyn, for the round-four push. This PR passes the endpoint's context window to OpenCode and Codex, launches Codex on the selected custom model (colon ids included), reads context sizes from /v1/models with a per-endpoint default that boot recovery keeps, and adds a search box to the model picker.

Every round-three item is in at this head: the in-use work moved to #757, this PR has its own changeset, the swap route checks the forced launch id before any write (and the new test proves the config dir survives a refused id), and the search box no longer takes focus on touch devices. Typecheck, lint, format, the frontend syntax check, the public-asset check, the CLI catalogue check and the touched test files are green here.

Three small things I will tidy at merge time unless you get to them first:

  • src/web/public/index.html:1037: the comment says the search box is "focused on every open"; since the touch guard it is focused only on non-touch devices.
  • docs/architecture-invariants.md:633: "its pattern widened to model-plain-colon" also narrows (a letter or digit first), which now applies to the native App Settings Codex model too. "changed to" with that half-sentence is accurate.
  • src/web/routes/session-routes.ts:1623: the launch-id check is pure now, so it can run before the context warning and the swap confirmation, which saves a user from confirming an eviction and then getting the 400. The injection.kind === 'unsupported' branch at :1624 cannot be reached after the check at :1536.

Two questions are still open on my side, not change requests:

Nothing else is needed from you on this PR.

@Ark0N
Ark0N merged commit 3bcb55c into Ark0N:master Nov 4, 2026
2 checks passed
Ark0N pushed a commit that referenced this pull request Nov 4, 2026
Replace the deepseek env redirect with a deepseek-patch-json recipe: a per-session DSH_HOME holding a generated cordis.patch.yml that declares a codeman-custom openai-completions provider over the installed profile. Ported from PR #752 commits 941df57 and 4bb1bfe, minus the core context-limit changes.
Ark0N pushed a commit that referenced this pull request Nov 4, 2026
The launch-id check is pure, so it now runs before the context warning and the
swap confirmation: nobody agrees to evict a model only to get the 400. Also the
search-box and in-use comments, the model-plain-colon wording, and the wiki's
order of the picker's marks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ark0N pushed a commit that referenced this pull request Nov 4, 2026
… a model (#752)

The dedicated route got this order at merge and quick-start kept the old one, so
a user could agree to a swap only to get the 400. It also runs on the pure build
now, so a refused id writes no config dir to clean up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Ark0N

Ark0N commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Thank you, Devvyn, this is in the 1.48.0 release. The launch-id check now runs before the context warning and the swap confirmation on both the swap route and quick-start, and the tidies from round four are in. The per-slot window question for Codex and OpenCode is still open on my side.

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.

OpenCode custom endpoints omit known context limits and overflow local servers

2 participants