From 9d6b9861757c826af83ae39cccd701cfc549cef2 Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sat, 1 Aug 2026 19:45:18 +0900 Subject: [PATCH 1/8] feat(server): advertise reasoning-effort ladders on the raw /v1/models list Grok Build discovers models through the plain OpenAI list shape at GET /v1/models (no client_version). Its model picker only enables the /effort control for entries that advertise the ladder in the Grok model catalog shape (supports_reasoning_effort + reasoning_efforts[] with value/label/default options), and --effort is ignored for models that do not advertise it. The raw list previously emitted only id/object/created/owned_by, so routed models appeared without effort controls even though the same tiers were already configured (reasoningEfforts / modelReasoningEfforts) and shipped on the Codex catalog branch (?client_version) and honored end-to-end by the chat-completions inbound path. Mirror the configured ladder onto raw list entries: entries with a non-empty reasoningEfforts list now also carry supports_reasoning_effort, reasoning_effort (the configured default, falling back to the first tier when the configured default is not in the visible list), and reasoning_efforts as {value, label, default} options. Entries with an empty tier list are unchanged. Extra fields are ignored by plain OpenAI clients. --- src/server/index.ts | 30 +++++++- tests/grok-models-effort-list.test.ts | 98 +++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 tests/grok-models-effort-list.test.ts diff --git a/src/server/index.ts b/src/server/index.ts index 5757d28cd1..d26468949b 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -524,9 +524,37 @@ export function startServer(port?: number) { } // OpenAI list shape: native gpt bare + routed models namespaced "/" // (pure availability list — disabled natives are omitted entirely). + // Grok Build discovers models through this endpoint too, and its model picker only + // enables /effort for entries that advertise the reasoning ladder in the Grok model + // catalog shape (supports_reasoning_effort + reasoning_efforts[]). The Codex catalog + // branch above already carries the same configured tiers, so mirror them here. Extra + // fields are ignored by plain OpenAI clients. + const grokEffortOption = (value: string, isDefault: boolean) => ({ + value, + label: `${value[0].toUpperCase()}${value.slice(1)} Effort`, + ...(isDefault ? { default: true } : {}), + }); const data = [ ...visibleNativeSlugs(config).map(id => ({ id, object: "model", created: 0, owned_by: "openai" })), - ...uniqueCatalogModelsForRawPublicList(goOrdered).map(m => ({ id: m.alias ?? `${m.provider}/${m.id}`, object: "model", created: 0, owned_by: m.owned_by ?? m.provider })), + ...uniqueCatalogModelsForRawPublicList(goOrdered).map(m => { + const efforts = m.reasoningEfforts ?? []; + const defaultEffort = m.defaultReasoningEffort && efforts.includes(m.defaultReasoningEffort) + ? m.defaultReasoningEffort + : efforts[0]; + return { + id: m.alias ?? `${m.provider}/${m.id}`, + object: "model", + created: 0, + owned_by: m.owned_by ?? m.provider, + ...(efforts.length > 0 + ? { + supports_reasoning_effort: true, + reasoning_effort: defaultEffort, + reasoning_efforts: efforts.map(effort => grokEffortOption(effort, effort === defaultEffort)), + } + : {}), + }; + }), ]; return jsonResponse({ object: "list", data }, 200, req, config); } diff --git a/tests/grok-models-effort-list.test.ts b/tests/grok-models-effort-list.test.ts new file mode 100644 index 0000000000..3528340648 --- /dev/null +++ b/tests/grok-models-effort-list.test.ts @@ -0,0 +1,98 @@ +import { afterEach, beforeEach, describe, expect, test } from "bun:test"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { saveConfig } from "../src/config"; +import { startServer } from "../src/server"; +import type { OcxConfig } from "../src/types"; + +const previousHome = process.env.OPENCODEX_HOME; +let testHome = ""; + +function effortConfig(): OcxConfig { + return { + port: 0, + hostname: "127.0.0.1", + defaultProvider: "kimi", + providers: { + kimi: { + adapter: "openai-chat", + baseUrl: "https://kimi.test/v1", + models: ["k3", "kimi-for-coding"], + modelReasoningEfforts: { + k3: ["low", "high", "max"], + "kimi-for-coding": [], + }, + modelDefaultReasoningEfforts: { k3: "high" }, + }, + }, + }; +} + +beforeEach(() => { + testHome = mkdtempSync(join(tmpdir(), "ocx-grok-effort-list-")); + process.env.OPENCODEX_HOME = testHome; +}); + +afterEach(() => { + if (previousHome === undefined) delete process.env.OPENCODEX_HOME; + else process.env.OPENCODEX_HOME = previousHome; + if (testHome) rmSync(testHome, { recursive: true, force: true }); + testHome = ""; +}); + +describe("raw /v1/models list reasoning-effort advertisement (Grok Build discovery)", () => { + test("routed models with configured tiers advertise the Grok reasoning catalog shape", async () => { + saveConfig(effortConfig()); + const server = startServer(0); + try { + const res = await fetch(new URL("/v1/models", server.url)); + expect(res.status).toBe(200); + const body = await res.json() as { data: Array> }; + const k3 = body.data.find(m => m.id === "kimi/k3"); + expect(k3).toBeDefined(); + expect(k3!.supports_reasoning_effort).toBe(true); + expect(k3!.reasoning_effort).toBe("high"); + expect(k3!.reasoning_efforts).toEqual([ + { value: "low", label: "Low Effort" }, + { value: "high", label: "High Effort", default: true }, + { value: "max", label: "Max Effort" }, + ]); + } finally { + await server.stop(true); + } + }); + + test("models with an empty tier list advertise no effort fields", async () => { + saveConfig(effortConfig()); + const server = startServer(0); + try { + const res = await fetch(new URL("/v1/models", server.url)); + const body = await res.json() as { data: Array> }; + const plain = body.data.find(m => m.id === "kimi/kimi-for-coding"); + expect(plain).toBeDefined(); + expect("supports_reasoning_effort" in plain!).toBe(false); + expect("reasoning_effort" in plain!).toBe(false); + expect("reasoning_efforts" in plain!).toBe(false); + } finally { + await server.stop(true); + } + }); + + test("an invalid configured default falls back to the first tier", async () => { + const config = effortConfig(); + config.providers.kimi!.modelDefaultReasoningEfforts = { k3: "medium" }; + saveConfig(config); + const server = startServer(0); + try { + const res = await fetch(new URL("/v1/models", server.url)); + const body = await res.json() as { data: Array> }; + const k3 = body.data.find(m => m.id === "kimi/k3"); + expect(k3!.reasoning_effort).toBe("low"); + const options = k3!.reasoning_efforts as Array>; + expect(options[0]).toEqual({ value: "low", label: "Low Effort", default: true }); + } finally { + await server.stop(true); + } + }); +}); From 6c900daff031cd809fc67a5b5ca8d82d72978f55 Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sat, 1 Aug 2026 19:49:15 +0900 Subject: [PATCH 2/8] docs(grok-build): document reasoning-effort advertisement on /v1/models --- docs-site/src/content/docs/guides/grok-build.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs-site/src/content/docs/guides/grok-build.md b/docs-site/src/content/docs/guides/grok-build.md index 08ad9f9f0e..c7b49fd2da 100644 --- a/docs-site/src/content/docs/guides/grok-build.md +++ b/docs-site/src/content/docs/guides/grok-build.md @@ -46,6 +46,17 @@ grok -m ocx-anthropic-claude-opus-4-8 -p "hello" # or in the TUI: /model ocx-anthropic-claude-opus-4-8 ``` +## Reasoning effort + +Grok Build's `/effort` (and `--effort`) only works for models whose catalog entry +advertises the ladder: its model list fetch reads the raw `GET /v1/models` response, and +entries there must carry `supports_reasoning_effort` plus `reasoning_efforts` menu +options. opencodex mirrors the configured provider tiers (`reasoningEfforts` / +`modelReasoningEfforts`, and the default from `modelDefaultReasoningEfforts`) onto that +response, so models with a configured ladder show the effort control in Grok Build just +like they do in Codex. Models with an empty tier list keep no effort control, matching +Codex behavior. + ## Authentication note Grok Build requires a non-empty API key for custom models even on loopback. The injected From 084b5b6c6418dae2058404a182d64c6094b55c37 Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:30:51 +0900 Subject: [PATCH 3/8] fix(server): use canonical default fallback and advertise native ladders too Address review feedback on the reasoning-effort advertisement: - Codex review (P2): the raw list now resolves the default with the same canonical fallback as the Codex catalog resolver in src/codex/catalog/effort.ts (configured default, then medium, then high, then the first tier) instead of always falling back to the first tier, so both branches advertise the same default for a model without an explicit modelDefaultReasoningEfforts entry. - Codex review (P2): native rows are decorated too, using the canonical native ladder from nativeReasoningEfforts (upstream snapshot, with max+ultra restored for the gpt-5.6 family), so auto-registered ocx-gpt-* native models also get a working /effort control in Grok Build. - CodeRabbit (docs): the Grok Build guide now states that the mirrored tiers describe the proxy-configured routed ladder, not native upstream reasoning support, and that adapters may emulate reasoning or map levels onto provider-specific fields. The shared grokEffortFields helper builds the Grok catalog shape for both row kinds; nativeReasoningEfforts is re-exported through the codex/catalog facade. Tests cover the native advertisement, the medium-first canonical fallback with and without a configured default, and the empty-tier omission. --- .../src/content/docs/guides/grok-build.md | 13 +- src/codex/catalog.ts | 2 +- src/server/index.ts | 263 ++++++------------ tests/grok-models-effort-list.test.ts | 31 ++- 4 files changed, 127 insertions(+), 182 deletions(-) diff --git a/docs-site/src/content/docs/guides/grok-build.md b/docs-site/src/content/docs/guides/grok-build.md index c7b49fd2da..3a4790da06 100644 --- a/docs-site/src/content/docs/guides/grok-build.md +++ b/docs-site/src/content/docs/guides/grok-build.md @@ -51,11 +51,14 @@ grok -m ocx-anthropic-claude-opus-4-8 -p "hello" Grok Build's `/effort` (and `--effort`) only works for models whose catalog entry advertises the ladder: its model list fetch reads the raw `GET /v1/models` response, and entries there must carry `supports_reasoning_effort` plus `reasoning_efforts` menu -options. opencodex mirrors the configured provider tiers (`reasoningEfforts` / -`modelReasoningEfforts`, and the default from `modelDefaultReasoningEfforts`) onto that -response, so models with a configured ladder show the effort control in Grok Build just -like they do in Codex. Models with an empty tier list keep no effort control, matching -Codex behavior. +options. For routed model entries, opencodex mirrors the configured provider tiers +(`reasoningEfforts` / `modelReasoningEfforts`, and the default from +`modelDefaultReasoningEfforts`) onto that response. This metadata describes the +proxy-configured routed ladder — it does not claim native upstream reasoning support, +and adapters may emulate reasoning or map levels onto provider-specific fields. Routed +models with a configured ladder show the effort control in Grok Build just like they do +in Codex. Models with an empty tier list keep no effort control, matching Codex +behavior. ## Authentication note diff --git a/src/codex/catalog.ts b/src/codex/catalog.ts index c6f21c750c..6f551e4a85 100644 --- a/src/codex/catalog.ts +++ b/src/codex/catalog.ts @@ -2,7 +2,7 @@ // Public surface preserved exactly; importers keep using "src/codex/catalog". export { isMediaGenerationModelId, shouldExposeRoutedModel, readCodexCatalogPath, readCatalog, normalizeRoutedCatalogEntry, catalogModelSlug, filterSupportedNativeSlugs, catalogModelSupportsReasoningSummaries } from "./catalog/parsing"; export type { CatalogModel, MultiAgentMode } from "./catalog/parsing"; -export { NATIVE_OPENAI_MODELS, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs } from "./catalog/metadata"; +export { NATIVE_OPENAI_MODELS, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeReasoningEfforts } from "./catalog/metadata"; export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled"; export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort"; export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithJawcodeMetadata } from "./catalog/provider-fetch"; diff --git a/src/server/index.ts b/src/server/index.ts index d26468949b..a28e804544 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -21,21 +21,6 @@ import { import { reconcileOAuthProviders } from "../oauth"; import { invalidateCodexModelsCache } from "../codex/catalog"; import { startMemoryWatchdog } from "./memory-watchdog"; -import { - reconcileLiveStateStores, - setLiveStateStoreConfig, -} from "../lib/state-store-registrations"; -import { startStateStoreSweeper } from "../lib/state-store-sweeper"; -import { - configureAppOwnedMemoryBudget, - enforceAppOwnedMemoryBudget, - resolveAppOwnedMemoryBudgetBytes, -} from "../lib/app-owned-memory"; -import { - registerAppOwnedMemorySweepFallback, - registerDefaultAppOwnedMemoryStores, - registerDefaultAppOwnedObservedBuffers, -} from "../lib/app-owned-memory-stores"; import { setStorageCleanupPolicyLiveSink } from "../storage/policy"; import { setStorageCleanupPolicyJobLiveApply } from "../storage/policy-job"; import { scheduleStorageCleanupStartupRun, startStorageCleanupScheduler } from "../storage/policy-scheduler"; @@ -54,8 +39,7 @@ export { resolveCodexAccountForThread, } from "../codex/routing"; import { formatCodexProviderForLog } from "../codex/routing"; -import { CatalogGatherBusyError } from "../codex/catalog/provider-fetch"; -import { registerCodexWebSocket, tryReserveCodexWebSocket, unregisterCodexWebSocket, updateCodexWebSocketAuthContext } from "../codex/websocket-registry"; +import { registerCodexWebSocket, unregisterCodexWebSocket, updateCodexWebSocketAuthContext } from "../codex/websocket-registry"; import { resolveGuiFilePath, rootFallbackPayload, serveGuiFile } from "./gui-static"; export { resolveGuiFilePath, rootFallbackPayload } from "./gui-static"; export { resolveAdapter } from "./adapter-resolve"; @@ -67,9 +51,7 @@ import { registerTurn, setServerRef, trackStreamLifetime, - tryAdmitTurn, unregisterTurn, - type ActiveTurnLease, } from "./lifecycle"; export { drainAndShutdown, @@ -273,14 +255,12 @@ function attachLiveSidebandUpstream(ws: ServerWebSocket): void { export function startServer(port?: number) { const config = runAlibabaRegionStartupMigration(runOpenAiTierStartupMigration(loadConfig())); - setLiveStateStoreConfig(config); applyProxyEnv(config); assertServerAuthConfig(config); const managementAuth = initializeManagementAuthState(config); // Refresh OAuth provider presets (models/noReasoningModels) from the registry so a proxy update // adding/dropping models reaches existing configs on start — not just fresh installs. reconcileOAuthProviders(config); - reconcileLiveStateStores(); // Seed default featured subagent models on first run only (UNSET → defaults). A user-set list, // even [], is left alone so GUI removals persist. if (config.subagentModels === undefined) { @@ -324,12 +304,6 @@ export function startServer(port?: number) { // #314: warn-only RSS observability (unref'd, idempotent — safe under repeated // startServer(0) in tests). Snapshot surfaces via GET /api/system/memory. startMemoryWatchdog(); - registerDefaultAppOwnedMemoryStores(); - registerDefaultAppOwnedObservedBuffers(); - registerAppOwnedMemorySweepFallback(); - configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(config.appOwnedMemoryBudgetMb)); - enforceAppOwnedMemoryBudget(); - startStateStoreSweeper(); // Issue #42 Phase 3: opt-in archived auto-cleanup (default OFF). Unref'd hourly // tick for daily/weekly; startup evaluation is fire-and-forget after listen. // Heavy work runs in a Worker via the single-flight job controller. @@ -363,31 +337,6 @@ export function startServer(port?: number) { return new Response(response.body, { status: 503, headers }); } - function serverBusyResponse(req: Request, resource: string): Response { - return withCors(new Response(JSON.stringify({ - error: { type: "server_error", code: "server_busy", message: `${resource} capacity reached` }, - }), { - status: 503, - headers: { "Content-Type": "application/json", "Retry-After": "1" }, - }), req, config); - } - - async function runAdmittedHttpTurn(req: Request, work: (lease: ActiveTurnLease) => Promise): Promise { - const lease = tryAdmitTurn(); - if (!lease) return serverBusyResponse(req, "active turns"); - let response: Response; - try { - response = await work(lease); - } catch (error) { - lease.release(); - throw error; - } - if (!lease.isTransferred()) { - lease.release(); - } - return response; - } - const server: Server = Bun.serve({ port: listenPort, hostname: bindHost, @@ -431,12 +380,9 @@ export function startServer(port?: number) { if (!websocketsEnabled(config)) { return withCors(formatErrorResponse(426, "upgrade_required", "Responses WebSocket transport is disabled; use HTTP"), req, config); } - const websocketLease = tryReserveCodexWebSocket(); - if (!websocketLease) return serverBusyResponse(req, "Codex WebSockets"); if (server.upgrade(req, { - data: buildResponsesWsData(selectForwardHeaders(req.headers), admission, websocketLease), + data: buildResponsesWsData(selectForwardHeaders(req.headers), admission), })) return undefined as unknown as Response; - websocketLease.release(); return withCors(formatErrorResponse(426, "upgrade_required", "WebSocket upgrade failed"), req, config); } @@ -462,19 +408,8 @@ export function startServer(port?: number) { if (!isAllowedRequestOrigin(req, config)) { return withCors(formatErrorResponse(403, "origin_rejected", "cross-origin data-plane request blocked"), req, config); } - let goModels; - try { - goModels = await fetchAllModels(config); - } catch (error) { - if (error instanceof CatalogGatherBusyError) { - return withCors(new Response(JSON.stringify({ error: { type: "server_error", code: "catalog_busy", message: error.message } }), { - status: 503, - headers: { "content-type": "application/json", "Retry-After": "1" }, - }), req, config); - } - throw error; - } - const { applyNativeVisibility, buildCatalogEntries, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, nativeOpenAiSlugs, orderForSubagents, filterCatalogVisibleModels, uniqueCatalogModelsForRawPublicList, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog"); + const goModels = await fetchAllModels(config); + const { applyNativeVisibility, buildCatalogEntries, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, nativeOpenAiSlugs, nativeReasoningEfforts, orderForSubagents, filterCatalogVisibleModels, uniqueCatalogModelsForRawPublicList, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog"); const nativeSlugs = nativeOpenAiSlugs(); const goEnabled = filterCatalogVisibleModels(goModels, config); const goOrdered = orderForSubagents(goEnabled, config.subagentModels); @@ -527,34 +462,42 @@ export function startServer(port?: number) { // Grok Build discovers models through this endpoint too, and its model picker only // enables /effort for entries that advertise the reasoning ladder in the Grok model // catalog shape (supports_reasoning_effort + reasoning_efforts[]). The Codex catalog - // branch above already carries the same configured tiers, so mirror them here. Extra - // fields are ignored by plain OpenAI clients. + // branch above already carries the same ladders, so mirror them here — native rows + // from the upstream snapshot, routed rows from the configured provider tiers. The + // default uses the same canonical fallback as the Codex catalog resolver + // (configured default, then medium, then high, then the first tier). Extra fields + // are ignored by plain OpenAI clients. const grokEffortOption = (value: string, isDefault: boolean) => ({ value, label: `${value[0].toUpperCase()}${value.slice(1)} Effort`, ...(isDefault ? { default: true } : {}), }); + const grokEffortFields = (efforts: string[], configuredDefault?: string) => { + if (efforts.length === 0) return {}; + const defaultEffort = configuredDefault && efforts.includes(configuredDefault) + ? configuredDefault + : efforts.includes("medium") ? "medium" : efforts.includes("high") ? "high" : efforts[0]; + return { + supports_reasoning_effort: true, + reasoning_effort: defaultEffort, + reasoning_efforts: efforts.map(effort => grokEffortOption(effort, effort === defaultEffort)), + }; + }; const data = [ - ...visibleNativeSlugs(config).map(id => ({ id, object: "model", created: 0, owned_by: "openai" })), - ...uniqueCatalogModelsForRawPublicList(goOrdered).map(m => { - const efforts = m.reasoningEfforts ?? []; - const defaultEffort = m.defaultReasoningEffort && efforts.includes(m.defaultReasoningEffort) - ? m.defaultReasoningEffort - : efforts[0]; - return { - id: m.alias ?? `${m.provider}/${m.id}`, - object: "model", - created: 0, - owned_by: m.owned_by ?? m.provider, - ...(efforts.length > 0 - ? { - supports_reasoning_effort: true, - reasoning_effort: defaultEffort, - reasoning_efforts: efforts.map(effort => grokEffortOption(effort, effort === defaultEffort)), - } - : {}), - }; - }), + ...visibleNativeSlugs(config).map(id => ({ + id, + object: "model", + created: 0, + owned_by: "openai", + ...grokEffortFields(nativeReasoningEfforts(id)), + })), + ...uniqueCatalogModelsForRawPublicList(goOrdered).map(m => ({ + id: m.alias ?? `${m.provider}/${m.id}`, + object: "model", + created: 0, + owned_by: m.owned_by ?? m.provider, + ...grokEffortFields(m.reasoningEfforts ?? [], m.defaultReasoningEffort), + })), ]; return jsonResponse({ object: "list", data }, 200, req, config); } @@ -579,17 +522,20 @@ export function startServer(port?: number) { ...admissionFields(admission), inboundProtocol: "responses", }; - return runAdmittedHttpTurn(req, async () => { - let response: Response; - try { - response = await handleResponsesCompact(req, config, logCtx); - } catch { - response = formatErrorResponse(500, "server_error", "Unexpected compact request failure"); - } - addFinalRequestLog(requestId, start, logCtx, response.status, - response.status === 499 ? { closeReason: "client_cancel" } : undefined); - return withCors(response, req, config); - }); + let response: Response; + try { + response = await handleResponsesCompact(req, config, logCtx); + } catch { + response = formatErrorResponse(500, "server_error", "Unexpected compact request failure"); + } + addFinalRequestLog( + requestId, + start, + logCtx, + response.status, + response.status === 499 ? { closeReason: "client_cancel" } : undefined, + ); + return withCors(response, req, config); } if ( @@ -613,11 +559,9 @@ export function startServer(port?: number) { ...admissionFields(admission), }; const endpoint = url.pathname.endsWith("/edits") ? "edits" as const : "generations" as const; - return runAdmittedHttpTurn(req, async () => { - const response = await handleImages(req, config, endpoint, logCtx); - addFinalRequestLog(requestId, start, logCtx, response.status, response.status === 499 ? { closeReason: "client_cancel" } : undefined); - return withCors(response, req, config); - }); + const response = await handleImages(req, config, endpoint, logCtx); + addFinalRequestLog(requestId, start, logCtx, response.status, response.status === 499 ? { closeReason: "client_cancel" } : undefined); + return withCors(response, req, config); } if (req.method === "GET" && url.pathname.startsWith("/v1/opencodex/artifacts/")) { @@ -667,12 +611,15 @@ export function startServer(port?: number) { provider: "unknown", ...admissionFields(admission), }; - return runAdmittedHttpTurn(req, async () => { - const response = await handleSearch(req, config, logCtx); - addFinalRequestLog(requestId, start, logCtx, response.status, - response.status === 499 ? { closeReason: "client_cancel" } : undefined); - return withCors(response, req, config); - }); + const response = await handleSearch(req, config, logCtx); + addFinalRequestLog( + requestId, + start, + logCtx, + response.status, + response.status === 499 ? { closeReason: "client_cancel" } : undefined, + ); + return withCors(response, req, config); } if (url.pathname === "/v1/responses" && req.method === "POST") { @@ -702,23 +649,20 @@ export function startServer(port?: number) { logged = true; addFinalRequestLog(requestId, start, logCtx, status, meta); }; - return runAdmittedHttpTurn(req, async turnAdmissionLease => { - const response = await handleResponses(req, config, logCtx, { - turnAdmissionLease, - abortSignal: req.signal, - onFirstOutput: () => recordFirstOutput(logCtx, start), - onNativePassthroughTerminal: status => { - finalizeNativePassthroughLog(httpStatusForTerminalStatus(status), { - terminalStatus: status, - closeReason: "terminal", - }); - }, - onNativePassthroughCancel: () => { - finalizeNativePassthroughLog(499, { closeReason: "client_cancel" }); - }, - }); - return withCors(responseWithDeferredRequestLog(response, requestId, start, logCtx), req, config); + const response = await handleResponses(req, config, logCtx, { + abortSignal: req.signal, + onFirstOutput: () => recordFirstOutput(logCtx, start), + onNativePassthroughTerminal: status => { + finalizeNativePassthroughLog(httpStatusForTerminalStatus(status), { + terminalStatus: status, + closeReason: "terminal", + }); + }, + onNativePassthroughCancel: () => { + finalizeNativePassthroughLog(499, { closeReason: "client_cancel" }); + }, }); + return withCors(responseWithDeferredRequestLog(response, requestId, start, logCtx), req, config); } // Anthropic Messages inbound (Claude Code). count_tokens FIRST (longer path). @@ -734,7 +678,8 @@ export function startServer(port?: number) { if (!isAllowedRequestOrigin(req, config)) { return withCors(anthropicErrorResponse(403, "cross-origin data-plane request blocked", "permission_error"), req, config); } - return runAdmittedHttpTurn(req, async () => withCors(await handleClaudeCountTokens(req, config), req, config)); + const response = await handleClaudeCountTokens(req, config); + return withCors(response, req, config); } if (url.pathname === "/v1/messages" && req.method === "POST") { @@ -760,11 +705,8 @@ export function startServer(port?: number) { // Logging is finalized inside handleClaudeMessages (Responses-vocab tap on the // pre-translation stream + native passthrough callbacks) — do not re-wrap the // translated Anthropic stream here. - return runAdmittedHttpTurn(req, async turnAdmissionLease => withCors( - await handleClaudeMessages(req, config, logCtx, { requestId, start, turnAdmissionLease }), - req, - config, - )); + const response = await handleClaudeMessages(req, config, logCtx, { requestId, start }); + return withCors(response, req, config); } @@ -787,11 +729,8 @@ export function startServer(port?: number) { ...admissionFields(admission), inboundProtocol: "chat", }; - return runAdmittedHttpTurn(req, async turnAdmissionLease => withCors( - await handleChatCompletions(req, config, logCtx, { requestId, start, turnAdmissionLease }), - req, - config, - )); + const response = await handleChatCompletions(req, config, logCtx, { requestId, start }); + return withCors(response, req, config); } // ChatGPT / Codex App voice (GPT‑Live / Frameless Bidi) + OpenAI Realtime call-create. @@ -817,17 +756,15 @@ export function startServer(port?: number) { provider: "unknown", ...admissionFields(admission), }; - return runAdmittedHttpTurn(req, async () => { - const response = await handleLive(req, config, logCtx); - addFinalRequestLog( - requestId, - start, - logCtx, - response.status, - response.status === 499 ? { closeReason: "client_cancel" } : undefined, - ); - return withCors(response, req, config); - }); + const response = await handleLive(req, config, logCtx); + addFinalRequestLog( + requestId, + start, + logCtx, + response.status, + response.status === 499 ? { closeReason: "client_cancel" } : undefined, + ); + return withCors(response, req, config); } // Voice / Realtime sideband WebSocket: Frameless joins /v1/live/{callId}; Realtime v1 joins @@ -899,11 +836,6 @@ export function startServer(port?: number) { attachLiveSidebandUpstream(ws); return; } - if (!ws.data.admissionLease) { - ws.close(1013, "server busy"); - return; - } - ws.data.admissionLease.bind(ws); registerCodexWebSocket(ws); }, message(ws: ServerWebSocket, raw: string | Buffer) { @@ -972,21 +904,9 @@ export function startServer(port?: number) { return; } - const turnAdmissionLease = tryAdmitTurn(); - if (!turnAdmissionLease) { - sendJsonFrame(ws, buildWsErrorFrame(503, { - type: "server_error", - code: "server_busy", - message: "active turns capacity reached", - retryable: true, - }, new Headers({ "Retry-After": "1" }))); - if (ws.data.cancel === cancelTurn) ws.data.cancel = undefined; - return; - } - const payload: Record = { ...frame }; delete payload.type; - turnAdmissionLease.bindAbortController(turnAbort); + registerTurn(turnAbort); void (async () => { const start = Date.now(); const requestId = nextRequestLogId(start); @@ -1023,7 +943,6 @@ export function startServer(port?: number) { const response = await handleResponses(req, config, logCtx, { forceEmptyResponseId: true, abortSignal: turnAbort.signal, - turnAdmissionLease, onFirstOutput: () => recordFirstOutput(logCtx, start), onCodexAuthContextResolved: context => updateCodexWebSocketAuthContext(ws, context), recordTerminalOutcomes: false, @@ -1064,7 +983,7 @@ export function startServer(port?: number) { /* socket already gone or send dropped */ } } finally { - turnAdmissionLease.release(); + unregisterTurn(turnAbort); if (!logged && turnAbort.signal.aborted) finalizeLog(499); if (ws.data.cancel === cancelTurn) ws.data.cancel = undefined; } @@ -1077,8 +996,6 @@ export function startServer(port?: number) { return; } unregisterCodexWebSocket(ws); - ws.data.admissionLease?.release(); - ws.data.admissionLease = undefined; ws.data.cancel?.(); // RC2: abort the upstream when the client disconnects }, }, diff --git a/tests/grok-models-effort-list.test.ts b/tests/grok-models-effort-list.test.ts index 3528340648..277ea3ab25 100644 --- a/tests/grok-models-effort-list.test.ts +++ b/tests/grok-models-effort-list.test.ts @@ -58,6 +58,12 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove { value: "high", label: "High Effort", default: true }, { value: "max", label: "Max Effort" }, ]); + // Native rows advertise the canonical upstream ladder with the same medium-first + // default the Codex catalog resolver uses, so /effort works for ocx-gpt-* too. + const native = body.data.find(m => m.id === "gpt-5.6-sol"); + expect(native).toBeDefined(); + expect(native!.supports_reasoning_effort).toBe(true); + expect(native!.reasoning_effort).toBe("medium"); } finally { await server.stop(true); } @@ -79,7 +85,25 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove } }); - test("an invalid configured default falls back to the first tier", async () => { + test("a ladder without a configured default uses the canonical medium default", async () => { + const config = effortConfig(); + config.providers.kimi!.modelDefaultReasoningEfforts = {}; + config.providers.kimi!.modelReasoningEfforts = { k3: ["low", "medium", "high"] }; + saveConfig(config); + const server = startServer(0); + try { + const res = await fetch(new URL("/v1/models", server.url)); + const body = await res.json() as { data: Array> }; + const k3 = body.data.find(m => m.id === "kimi/k3"); + expect(k3!.reasoning_effort).toBe("medium"); + const options = k3!.reasoning_efforts as Array>; + expect(options[1]).toEqual({ value: "medium", label: "Medium Effort", default: true }); + } finally { + await server.stop(true); + } + }); + + test("an invalid configured default falls back with the canonical medium/high/first order", async () => { const config = effortConfig(); config.providers.kimi!.modelDefaultReasoningEfforts = { k3: "medium" }; saveConfig(config); @@ -88,9 +112,10 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove const res = await fetch(new URL("/v1/models", server.url)); const body = await res.json() as { data: Array> }; const k3 = body.data.find(m => m.id === "kimi/k3"); - expect(k3!.reasoning_effort).toBe("low"); + // k3's ladder is low/high/max: no medium, so the canonical fallback picks high. + expect(k3!.reasoning_effort).toBe("high"); const options = k3!.reasoning_efforts as Array>; - expect(options[0]).toEqual({ value: "low", label: "Low Effort", default: true }); + expect(options[1]).toEqual({ value: "high", label: "High Effort", default: true }); } finally { await server.stop(true); } From 68aa3f3ed24f14173ffb7a24fc0a98754684d325 Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:52:09 +0900 Subject: [PATCH 4/8] docs(test): distinguish native ladders and pin regression coverage Address the follow-up CodeRabbit review: - Document that native GPT-5.6 rows expose their pinned upstream reasoning ladders, separately from routed entries whose tiers come from proxy provider configuration and may be mapped or emulated by adapters. - Assert the complete gpt-5.6-sol ladder (low through ultra), not only the support flag and medium default, so max/ultra cannot regress silently. --- docs-site/src/content/docs/guides/grok-build.md | 3 ++- tests/grok-models-effort-list.test.ts | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs-site/src/content/docs/guides/grok-build.md b/docs-site/src/content/docs/guides/grok-build.md index 3a4790da06..9b1fc1bf64 100644 --- a/docs-site/src/content/docs/guides/grok-build.md +++ b/docs-site/src/content/docs/guides/grok-build.md @@ -58,7 +58,8 @@ proxy-configured routed ladder — it does not claim native upstream reasoning s and adapters may emulate reasoning or map levels onto provider-specific fields. Routed models with a configured ladder show the effort control in Grok Build just like they do in Codex. Models with an empty tier list keep no effort control, matching Codex -behavior. +behavior. Native GPT-5.6 entries are separate: they preserve and expose their pinned +upstream reasoning ladders rather than provider-configured routed metadata. ## Authentication note diff --git a/tests/grok-models-effort-list.test.ts b/tests/grok-models-effort-list.test.ts index 277ea3ab25..da272f4ccd 100644 --- a/tests/grok-models-effort-list.test.ts +++ b/tests/grok-models-effort-list.test.ts @@ -64,6 +64,9 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove expect(native).toBeDefined(); expect(native!.supports_reasoning_effort).toBe(true); expect(native!.reasoning_effort).toBe("medium"); + expect((native!.reasoning_efforts as Array<{ value: string }>).map(option => option.value)).toEqual([ + "low", "medium", "high", "xhigh", "max", "ultra", + ]); } finally { await server.stop(true); } From 0938eaf4e4d58a887a034660c4f5ad6a051d17b9 Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:53:48 +0900 Subject: [PATCH 5/8] rebase: preserve latest dev catalog busy handling --- src/server/index.ts | 209 +++++++++++++++++++++++++++++++------------- 1 file changed, 150 insertions(+), 59 deletions(-) diff --git a/src/server/index.ts b/src/server/index.ts index a28e804544..776eeb4202 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -21,6 +21,21 @@ import { import { reconcileOAuthProviders } from "../oauth"; import { invalidateCodexModelsCache } from "../codex/catalog"; import { startMemoryWatchdog } from "./memory-watchdog"; +import { + reconcileLiveStateStores, + setLiveStateStoreConfig, +} from "../lib/state-store-registrations"; +import { startStateStoreSweeper } from "../lib/state-store-sweeper"; +import { + configureAppOwnedMemoryBudget, + enforceAppOwnedMemoryBudget, + resolveAppOwnedMemoryBudgetBytes, +} from "../lib/app-owned-memory"; +import { + registerAppOwnedMemorySweepFallback, + registerDefaultAppOwnedMemoryStores, + registerDefaultAppOwnedObservedBuffers, +} from "../lib/app-owned-memory-stores"; import { setStorageCleanupPolicyLiveSink } from "../storage/policy"; import { setStorageCleanupPolicyJobLiveApply } from "../storage/policy-job"; import { scheduleStorageCleanupStartupRun, startStorageCleanupScheduler } from "../storage/policy-scheduler"; @@ -39,7 +54,8 @@ export { resolveCodexAccountForThread, } from "../codex/routing"; import { formatCodexProviderForLog } from "../codex/routing"; -import { registerCodexWebSocket, unregisterCodexWebSocket, updateCodexWebSocketAuthContext } from "../codex/websocket-registry"; +import { CatalogGatherBusyError } from "../codex/catalog/provider-fetch"; +import { registerCodexWebSocket, tryReserveCodexWebSocket, unregisterCodexWebSocket, updateCodexWebSocketAuthContext } from "../codex/websocket-registry"; import { resolveGuiFilePath, rootFallbackPayload, serveGuiFile } from "./gui-static"; export { resolveGuiFilePath, rootFallbackPayload } from "./gui-static"; export { resolveAdapter } from "./adapter-resolve"; @@ -51,7 +67,9 @@ import { registerTurn, setServerRef, trackStreamLifetime, + tryAdmitTurn, unregisterTurn, + type ActiveTurnLease, } from "./lifecycle"; export { drainAndShutdown, @@ -255,12 +273,14 @@ function attachLiveSidebandUpstream(ws: ServerWebSocket): void { export function startServer(port?: number) { const config = runAlibabaRegionStartupMigration(runOpenAiTierStartupMigration(loadConfig())); + setLiveStateStoreConfig(config); applyProxyEnv(config); assertServerAuthConfig(config); const managementAuth = initializeManagementAuthState(config); // Refresh OAuth provider presets (models/noReasoningModels) from the registry so a proxy update // adding/dropping models reaches existing configs on start — not just fresh installs. reconcileOAuthProviders(config); + reconcileLiveStateStores(); // Seed default featured subagent models on first run only (UNSET → defaults). A user-set list, // even [], is left alone so GUI removals persist. if (config.subagentModels === undefined) { @@ -304,6 +324,12 @@ export function startServer(port?: number) { // #314: warn-only RSS observability (unref'd, idempotent — safe under repeated // startServer(0) in tests). Snapshot surfaces via GET /api/system/memory. startMemoryWatchdog(); + registerDefaultAppOwnedMemoryStores(); + registerDefaultAppOwnedObservedBuffers(); + registerAppOwnedMemorySweepFallback(); + configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(config.appOwnedMemoryBudgetMb)); + enforceAppOwnedMemoryBudget(); + startStateStoreSweeper(); // Issue #42 Phase 3: opt-in archived auto-cleanup (default OFF). Unref'd hourly // tick for daily/weekly; startup evaluation is fire-and-forget after listen. // Heavy work runs in a Worker via the single-flight job controller. @@ -337,6 +363,31 @@ export function startServer(port?: number) { return new Response(response.body, { status: 503, headers }); } + function serverBusyResponse(req: Request, resource: string): Response { + return withCors(new Response(JSON.stringify({ + error: { type: "server_error", code: "server_busy", message: `${resource} capacity reached` }, + }), { + status: 503, + headers: { "Content-Type": "application/json", "Retry-After": "1" }, + }), req, config); + } + + async function runAdmittedHttpTurn(req: Request, work: (lease: ActiveTurnLease) => Promise): Promise { + const lease = tryAdmitTurn(); + if (!lease) return serverBusyResponse(req, "active turns"); + let response: Response; + try { + response = await work(lease); + } catch (error) { + lease.release(); + throw error; + } + if (!lease.isTransferred()) { + lease.release(); + } + return response; + } + const server: Server = Bun.serve({ port: listenPort, hostname: bindHost, @@ -380,9 +431,12 @@ export function startServer(port?: number) { if (!websocketsEnabled(config)) { return withCors(formatErrorResponse(426, "upgrade_required", "Responses WebSocket transport is disabled; use HTTP"), req, config); } + const websocketLease = tryReserveCodexWebSocket(); + if (!websocketLease) return serverBusyResponse(req, "Codex WebSockets"); if (server.upgrade(req, { - data: buildResponsesWsData(selectForwardHeaders(req.headers), admission), + data: buildResponsesWsData(selectForwardHeaders(req.headers), admission, websocketLease), })) return undefined as unknown as Response; + websocketLease.release(); return withCors(formatErrorResponse(426, "upgrade_required", "WebSocket upgrade failed"), req, config); } @@ -408,7 +462,18 @@ export function startServer(port?: number) { if (!isAllowedRequestOrigin(req, config)) { return withCors(formatErrorResponse(403, "origin_rejected", "cross-origin data-plane request blocked"), req, config); } - const goModels = await fetchAllModels(config); + let goModels; + try { + goModels = await fetchAllModels(config); + } catch (error) { + if (error instanceof CatalogGatherBusyError) { + return withCors(new Response(JSON.stringify({ error: { type: "server_error", code: "catalog_busy", message: error.message } }), { + status: 503, + headers: { "content-type": "application/json", "Retry-After": "1" }, + }), req, config); + } + throw error; + } const { applyNativeVisibility, buildCatalogEntries, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, nativeOpenAiSlugs, nativeReasoningEfforts, orderForSubagents, filterCatalogVisibleModels, uniqueCatalogModelsForRawPublicList, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog"); const nativeSlugs = nativeOpenAiSlugs(); const goEnabled = filterCatalogVisibleModels(goModels, config); @@ -522,20 +587,17 @@ export function startServer(port?: number) { ...admissionFields(admission), inboundProtocol: "responses", }; - let response: Response; - try { - response = await handleResponsesCompact(req, config, logCtx); - } catch { - response = formatErrorResponse(500, "server_error", "Unexpected compact request failure"); - } - addFinalRequestLog( - requestId, - start, - logCtx, - response.status, - response.status === 499 ? { closeReason: "client_cancel" } : undefined, - ); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async () => { + let response: Response; + try { + response = await handleResponsesCompact(req, config, logCtx); + } catch { + response = formatErrorResponse(500, "server_error", "Unexpected compact request failure"); + } + addFinalRequestLog(requestId, start, logCtx, response.status, + response.status === 499 ? { closeReason: "client_cancel" } : undefined); + return withCors(response, req, config); + }); } if ( @@ -559,9 +621,11 @@ export function startServer(port?: number) { ...admissionFields(admission), }; const endpoint = url.pathname.endsWith("/edits") ? "edits" as const : "generations" as const; - const response = await handleImages(req, config, endpoint, logCtx); - addFinalRequestLog(requestId, start, logCtx, response.status, response.status === 499 ? { closeReason: "client_cancel" } : undefined); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async () => { + const response = await handleImages(req, config, endpoint, logCtx); + addFinalRequestLog(requestId, start, logCtx, response.status, response.status === 499 ? { closeReason: "client_cancel" } : undefined); + return withCors(response, req, config); + }); } if (req.method === "GET" && url.pathname.startsWith("/v1/opencodex/artifacts/")) { @@ -611,15 +675,12 @@ export function startServer(port?: number) { provider: "unknown", ...admissionFields(admission), }; - const response = await handleSearch(req, config, logCtx); - addFinalRequestLog( - requestId, - start, - logCtx, - response.status, - response.status === 499 ? { closeReason: "client_cancel" } : undefined, - ); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async () => { + const response = await handleSearch(req, config, logCtx); + addFinalRequestLog(requestId, start, logCtx, response.status, + response.status === 499 ? { closeReason: "client_cancel" } : undefined); + return withCors(response, req, config); + }); } if (url.pathname === "/v1/responses" && req.method === "POST") { @@ -649,20 +710,23 @@ export function startServer(port?: number) { logged = true; addFinalRequestLog(requestId, start, logCtx, status, meta); }; - const response = await handleResponses(req, config, logCtx, { - abortSignal: req.signal, - onFirstOutput: () => recordFirstOutput(logCtx, start), - onNativePassthroughTerminal: status => { - finalizeNativePassthroughLog(httpStatusForTerminalStatus(status), { - terminalStatus: status, - closeReason: "terminal", - }); - }, - onNativePassthroughCancel: () => { - finalizeNativePassthroughLog(499, { closeReason: "client_cancel" }); - }, + return runAdmittedHttpTurn(req, async turnAdmissionLease => { + const response = await handleResponses(req, config, logCtx, { + turnAdmissionLease, + abortSignal: req.signal, + onFirstOutput: () => recordFirstOutput(logCtx, start), + onNativePassthroughTerminal: status => { + finalizeNativePassthroughLog(httpStatusForTerminalStatus(status), { + terminalStatus: status, + closeReason: "terminal", + }); + }, + onNativePassthroughCancel: () => { + finalizeNativePassthroughLog(499, { closeReason: "client_cancel" }); + }, + }); + return withCors(responseWithDeferredRequestLog(response, requestId, start, logCtx), req, config); }); - return withCors(responseWithDeferredRequestLog(response, requestId, start, logCtx), req, config); } // Anthropic Messages inbound (Claude Code). count_tokens FIRST (longer path). @@ -678,8 +742,7 @@ export function startServer(port?: number) { if (!isAllowedRequestOrigin(req, config)) { return withCors(anthropicErrorResponse(403, "cross-origin data-plane request blocked", "permission_error"), req, config); } - const response = await handleClaudeCountTokens(req, config); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async () => withCors(await handleClaudeCountTokens(req, config), req, config)); } if (url.pathname === "/v1/messages" && req.method === "POST") { @@ -705,8 +768,11 @@ export function startServer(port?: number) { // Logging is finalized inside handleClaudeMessages (Responses-vocab tap on the // pre-translation stream + native passthrough callbacks) — do not re-wrap the // translated Anthropic stream here. - const response = await handleClaudeMessages(req, config, logCtx, { requestId, start }); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async turnAdmissionLease => withCors( + await handleClaudeMessages(req, config, logCtx, { requestId, start, turnAdmissionLease }), + req, + config, + )); } @@ -729,8 +795,11 @@ export function startServer(port?: number) { ...admissionFields(admission), inboundProtocol: "chat", }; - const response = await handleChatCompletions(req, config, logCtx, { requestId, start }); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async turnAdmissionLease => withCors( + await handleChatCompletions(req, config, logCtx, { requestId, start, turnAdmissionLease }), + req, + config, + )); } // ChatGPT / Codex App voice (GPT‑Live / Frameless Bidi) + OpenAI Realtime call-create. @@ -756,15 +825,17 @@ export function startServer(port?: number) { provider: "unknown", ...admissionFields(admission), }; - const response = await handleLive(req, config, logCtx); - addFinalRequestLog( - requestId, - start, - logCtx, - response.status, - response.status === 499 ? { closeReason: "client_cancel" } : undefined, - ); - return withCors(response, req, config); + return runAdmittedHttpTurn(req, async () => { + const response = await handleLive(req, config, logCtx); + addFinalRequestLog( + requestId, + start, + logCtx, + response.status, + response.status === 499 ? { closeReason: "client_cancel" } : undefined, + ); + return withCors(response, req, config); + }); } // Voice / Realtime sideband WebSocket: Frameless joins /v1/live/{callId}; Realtime v1 joins @@ -836,6 +907,11 @@ export function startServer(port?: number) { attachLiveSidebandUpstream(ws); return; } + if (!ws.data.admissionLease) { + ws.close(1013, "server busy"); + return; + } + ws.data.admissionLease.bind(ws); registerCodexWebSocket(ws); }, message(ws: ServerWebSocket, raw: string | Buffer) { @@ -904,9 +980,21 @@ export function startServer(port?: number) { return; } + const turnAdmissionLease = tryAdmitTurn(); + if (!turnAdmissionLease) { + sendJsonFrame(ws, buildWsErrorFrame(503, { + type: "server_error", + code: "server_busy", + message: "active turns capacity reached", + retryable: true, + }, new Headers({ "Retry-After": "1" }))); + if (ws.data.cancel === cancelTurn) ws.data.cancel = undefined; + return; + } + const payload: Record = { ...frame }; delete payload.type; - registerTurn(turnAbort); + turnAdmissionLease.bindAbortController(turnAbort); void (async () => { const start = Date.now(); const requestId = nextRequestLogId(start); @@ -943,6 +1031,7 @@ export function startServer(port?: number) { const response = await handleResponses(req, config, logCtx, { forceEmptyResponseId: true, abortSignal: turnAbort.signal, + turnAdmissionLease, onFirstOutput: () => recordFirstOutput(logCtx, start), onCodexAuthContextResolved: context => updateCodexWebSocketAuthContext(ws, context), recordTerminalOutcomes: false, @@ -983,7 +1072,7 @@ export function startServer(port?: number) { /* socket already gone or send dropped */ } } finally { - unregisterTurn(turnAbort); + turnAdmissionLease.release(); if (!logged && turnAbort.signal.aborted) finalizeLog(499); if (ws.data.cancel === cancelTurn) ws.data.cancel = undefined; } @@ -996,6 +1085,8 @@ export function startServer(port?: number) { return; } unregisterCodexWebSocket(ws); + ws.data.admissionLease?.release(); + ws.data.admissionLease = undefined; ws.data.cancel?.(); // RC2: abort the upstream when the client disconnects }, }, From 30c7e97f2086dfc3c62b40a270eccc5f8309df9a Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sat, 1 Aug 2026 21:06:52 +0900 Subject: [PATCH 6/8] test(server): cover the first-tier reasoning default fallback Address the latest CodeRabbit coverage request by exercising the final branch of the canonical configured -> medium -> high -> first fallback. Use a custom routed model with only low/max tiers so provider registry wire-map self-healing does not add a high tier, then assert low is the sole default option. --- tests/grok-models-effort-list.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/grok-models-effort-list.test.ts b/tests/grok-models-effort-list.test.ts index da272f4ccd..dd42f22648 100644 --- a/tests/grok-models-effort-list.test.ts +++ b/tests/grok-models-effort-list.test.ts @@ -123,4 +123,25 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove await server.stop(true); } }); + + test("falls back to the first tier when neither medium nor high is available", async () => { + const config = effortConfig(); + config.providers.kimi!.models = [...(config.providers.kimi!.models ?? []), "custom-test"]; + config.providers.kimi!.modelReasoningEfforts = { "custom-test": ["low", "max"] }; + config.providers.kimi!.modelDefaultReasoningEfforts = { "custom-test": "medium" }; + saveConfig(config); + const server = startServer(0); + try { + const res = await fetch(new URL("/v1/models", server.url)); + const body = await res.json() as { data: Array> }; + const model = body.data.find(m => m.id === "kimi/custom-test"); + expect(model!.reasoning_effort).toBe("low"); + expect(model!.reasoning_efforts).toEqual([ + { value: "low", label: "Low Effort", default: true }, + { value: "max", label: "Max Effort" }, + ]); + } finally { + await server.stop(true); + } + }); }); From 38da791231b0b10f53e760fedb028de9927f1efb Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:30:03 +0200 Subject: [PATCH 7/8] fix(server): preserve upstream native default on raw /v1/models effort ads gpt-5.6-sol pins default_reasoning_level=low in the Codex catalog path, but the raw list always fell through to medium. Pass the upstream native default into grokEffortFields so both discovery surfaces advertise the same default. --- src/codex/catalog.ts | 2 +- src/codex/catalog/metadata.ts | 6 ++++++ src/server/index.ts | 4 ++-- tests/grok-models-effort-list.test.ts | 6 +++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/codex/catalog.ts b/src/codex/catalog.ts index 6f551e4a85..a410b59b05 100644 --- a/src/codex/catalog.ts +++ b/src/codex/catalog.ts @@ -2,7 +2,7 @@ // Public surface preserved exactly; importers keep using "src/codex/catalog". export { isMediaGenerationModelId, shouldExposeRoutedModel, readCodexCatalogPath, readCatalog, normalizeRoutedCatalogEntry, catalogModelSlug, filterSupportedNativeSlugs, catalogModelSupportsReasoningSummaries } from "./catalog/parsing"; export type { CatalogModel, MultiAgentMode } from "./catalog/parsing"; -export { NATIVE_OPENAI_MODELS, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeReasoningEfforts } from "./catalog/metadata"; +export { NATIVE_OPENAI_MODELS, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeReasoningEfforts, nativeDefaultReasoningEffort } from "./catalog/metadata"; export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled"; export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort"; export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithJawcodeMetadata } from "./catalog/provider-fetch"; diff --git a/src/codex/catalog/metadata.ts b/src/codex/catalog/metadata.ts index 4f4786e171..7c9dcf6e24 100644 --- a/src/codex/catalog/metadata.ts +++ b/src/codex/catalog/metadata.ts @@ -103,6 +103,12 @@ export function nativeReasoningEfforts(slug: string): string[] { return ["low", "medium", "high", "xhigh"]; } +/** Upstream-pinned default for a native slug, when present and non-empty. */ +export function nativeDefaultReasoningEffort(slug: string): string | undefined { + const level = UPSTREAM_NATIVE_ENTRIES.get(slug)?.default_reasoning_level; + return typeof level === "string" && level.length > 0 ? level : undefined; +} + export function nativeParallelToolCalls(slug: string): boolean { return UPSTREAM_NATIVE_ENTRIES.get(slug)?.supports_parallel_tool_calls === true || false; diff --git a/src/server/index.ts b/src/server/index.ts index 776eeb4202..c8d7968c1d 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -474,7 +474,7 @@ export function startServer(port?: number) { } throw error; } - const { applyNativeVisibility, buildCatalogEntries, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, nativeOpenAiSlugs, nativeReasoningEfforts, orderForSubagents, filterCatalogVisibleModels, uniqueCatalogModelsForRawPublicList, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog"); + const { applyNativeVisibility, buildCatalogEntries, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, nativeOpenAiSlugs, nativeReasoningEfforts, nativeDefaultReasoningEffort, orderForSubagents, filterCatalogVisibleModels, uniqueCatalogModelsForRawPublicList, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog"); const nativeSlugs = nativeOpenAiSlugs(); const goEnabled = filterCatalogVisibleModels(goModels, config); const goOrdered = orderForSubagents(goEnabled, config.subagentModels); @@ -554,7 +554,7 @@ export function startServer(port?: number) { object: "model", created: 0, owned_by: "openai", - ...grokEffortFields(nativeReasoningEfforts(id)), + ...grokEffortFields(nativeReasoningEfforts(id), nativeDefaultReasoningEffort(id)), })), ...uniqueCatalogModelsForRawPublicList(goOrdered).map(m => ({ id: m.alias ?? `${m.provider}/${m.id}`, diff --git a/tests/grok-models-effort-list.test.ts b/tests/grok-models-effort-list.test.ts index dd42f22648..48fa329271 100644 --- a/tests/grok-models-effort-list.test.ts +++ b/tests/grok-models-effort-list.test.ts @@ -58,12 +58,12 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove { value: "high", label: "High Effort", default: true }, { value: "max", label: "Max Effort" }, ]); - // Native rows advertise the canonical upstream ladder with the same medium-first - // default the Codex catalog resolver uses, so /effort works for ocx-gpt-* too. + // Native rows advertise the upstream ladder and preserve the pinned upstream default + // (gpt-5.6-sol => low), matching the Codex catalog path. const native = body.data.find(m => m.id === "gpt-5.6-sol"); expect(native).toBeDefined(); expect(native!.supports_reasoning_effort).toBe(true); - expect(native!.reasoning_effort).toBe("medium"); + expect(native!.reasoning_effort).toBe("low"); expect((native!.reasoning_efforts as Array<{ value: string }>).map(option => option.value)).toEqual([ "low", "medium", "high", "xhigh", "max", "ultra", ]); From 53d837405d1d2736034264e4ac85012a97090d8a Mon Sep 17 00:00:00 2001 From: n3wr1ch <40690535+n3wr1ch@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:12:09 +0900 Subject: [PATCH 8/8] fix(catalog): preserve per-model GPT-5.6 effort ladders --- src/codex/catalog/metadata.ts | 14 +++-------- tests/grok-models-effort-list.test.ts | 36 ++++++++++++++++++++------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/codex/catalog/metadata.ts b/src/codex/catalog/metadata.ts index 7c9dcf6e24..8f3bad4942 100644 --- a/src/codex/catalog/metadata.ts +++ b/src/codex/catalog/metadata.ts @@ -34,7 +34,6 @@ import upstreamModelsSnapshot from "../data/upstream-models.json"; import { filterSupportedNativeSlugs } from "./parsing"; import type { RawEntry } from "./parsing"; import { readCurrentCatalogOrCache, unique } from "./bundled"; -import { ensureGpt56ReasoningLevels, isGpt56NativeSlug } from "./effort"; export const NATIVE_OPENAI_MODELS = [ "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark", @@ -88,16 +87,9 @@ export function nativeReasoningEfforts(slug: string): string[] { ? upstream!.supported_reasoning_levels as Array<{ effort?: string }> : []; if (levels.length > 0) { - const efforts = levels.flatMap(l => typeof l.effort === "string" ? [l.effort] : []); - // gpt-5.6 natives get max+ultra restored (ensureGpt56ReasoningLevels catalog path does - // the same); older natives (gpt-5.5/5.4/5.4-mini/5.3-codex-spark) stop at xhigh per - // upstream snapshot. - if (isGpt56NativeSlug(slug)) { - const set = new Set(efforts); - for (const e of ["max", "ultra"]) set.add(e); - return [...set]; - } - return efforts; + // Preserve the exact pinned per-model ladder. In particular, GPT-5.6 Sol and Terra + // include ultra while Luna intentionally ends at max. + return levels.flatMap(l => typeof l.effort === "string" ? [l.effort] : []); } // gpt-5.3-codex-spark is not in upstream snapshot — use the standard old-ladder default. return ["low", "medium", "high", "xhigh"]; diff --git a/tests/grok-models-effort-list.test.ts b/tests/grok-models-effort-list.test.ts index 48fa329271..5c24e93e53 100644 --- a/tests/grok-models-effort-list.test.ts +++ b/tests/grok-models-effort-list.test.ts @@ -58,15 +58,33 @@ describe("raw /v1/models list reasoning-effort advertisement (Grok Build discove { value: "high", label: "High Effort", default: true }, { value: "max", label: "Max Effort" }, ]); - // Native rows advertise the upstream ladder and preserve the pinned upstream default - // (gpt-5.6-sol => low), matching the Codex catalog path. - const native = body.data.find(m => m.id === "gpt-5.6-sol"); - expect(native).toBeDefined(); - expect(native!.supports_reasoning_effort).toBe(true); - expect(native!.reasoning_effort).toBe("low"); - expect((native!.reasoning_efforts as Array<{ value: string }>).map(option => option.value)).toEqual([ - "low", "medium", "high", "xhigh", "max", "ultra", - ]); + // Native rows preserve the pinned per-model ladder and default. Sol and Terra include + // ultra, while Luna intentionally ends at max, matching the canonical Codex catalog. + const nativeExpectations = [ + { + id: "gpt-5.6-sol", + defaultEffort: "low", + efforts: ["low", "medium", "high", "xhigh", "max", "ultra"], + }, + { + id: "gpt-5.6-terra", + defaultEffort: "medium", + efforts: ["low", "medium", "high", "xhigh", "max", "ultra"], + }, + { + id: "gpt-5.6-luna", + defaultEffort: "medium", + efforts: ["low", "medium", "high", "xhigh", "max"], + }, + ]; + for (const expected of nativeExpectations) { + const native = body.data.find(m => m.id === expected.id); + expect(native).toBeDefined(); + expect(native!.supports_reasoning_effort).toBe(true); + expect(native!.reasoning_effort).toBe(expected.defaultEffort); + expect((native!.reasoning_efforts as Array<{ value: string }>).map(option => option.value)) + .toEqual(expected.efforts); + } } finally { await server.stop(true); }