@oh-my-pi/pi-catalog 17.3.5 → 17.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/types/discovery/cursor-gen/agent_pb.d.ts +88 -3
- package/dist/types/identity/family.d.ts +13 -1
- package/dist/types/model-thinking.d.ts +6 -2
- package/dist/types/provider-models/descriptors.d.ts +3 -2
- package/dist/types/types.d.ts +12 -1
- package/dist/types/variant-collapse.d.ts +2 -0
- package/package.json +4 -4
- package/src/compat/openai.ts +27 -1
- package/src/discovery/codex.ts +133 -29
- package/src/discovery/cursor-gen/agent_pb.ts +614 -519
- package/src/discovery/cursor.ts +14 -1
- package/src/identity/family.ts +21 -1
- package/src/model-cache.ts +67 -6
- package/src/model-thinking.ts +38 -6
- package/src/models.json +685 -215
- package/src/provider-models/cache-provider-id.ts +3 -1
- package/src/provider-models/descriptors.ts +3 -2
- package/src/provider-models/openai-compat.ts +83 -3
- package/src/types.ts +12 -0
- package/src/variant-collapse.ts +67 -34
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.3.8] - 2026-08-19
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a Cursor variant-collapse table folding the per-effort Grok siblings (`cursor-grok-4.5` low/medium/high and `cursor-grok-4.6` low/medium/high/xhigh, plus their `-fast` service-tier lanes) into one logical model per lane with effort routing onto the live wire ids, matching Devin's `grok-4-5` collapse ([#8803](https://github.com/can1357/oh-my-pi/issues/8803)).
|
|
10
|
+
- Regenerated the Cursor agent protobufs to model hosted WebFetch permission queries (`interaction_query` / `interaction_response` field 9) and the matching `ToolCall` variant (field 37).
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed a physically corrupt `models.db` (`SQLITE_CORRUPT*` / `SQLITE_NOTADB`, "database disk image is malformed") permanently disabling the model cache. The shared read/write paths swallowed unrecoverable SQLite corruption as a best-effort miss and cached the broken handle, so a successful live catalog could never overwrite the corrupt cache and every later process repeated the miss — a runtime provider extension with no bundled catalog was left with only its bootstrap model. Corruption now self-heals: the cache closes the handle, quarantines `models.db`(+`-wal`/`-shm`) aside, recreates a fresh database, and retries the operation once; `SQLITE_BUSY`, permission, and unrelated errors keep their existing best-effort paths ([#8867](https://github.com/can1357/oh-my-pi/issues/8867)).
|
|
15
|
+
- Fixed local Qwen 3.8+ models (llama.cpp, vLLM, loopback custom providers) exposing the generic `minimal..high` thinking ladder instead of the chat template's real `low`/`medium`/`xhigh` `reasoning_effort` tiers. The derived metadata now marks thinking as mandatory (the official 3.8 template raises on `enable_thinking: false`), vLLM-served Qwen routes through the `chat_template_kwargs` dialect (top-level `enable_thinking` is ignored by vLLM), and vLLM discovery lights up the reasoning dial for Qwen 3.8+ ids its `/v1/models` endpoint reports as non-reasoning.
|
|
16
|
+
- Fixed `deepseek-v4-pro-0813` surfacing from Alibaba Token Plan discovery with `contextWindow`/`maxTokens` of `null`. The dated DeepSeek V4 Pro snapshot was missing from `ALIBABA_TOKEN_PLAN_DISCOVERED_MODEL_LIMITS`, so unlike its `deepseek-v4-flash-0731` sibling it fell through to unknown limits ([#8847](https://github.com/can1357/oh-my-pi/issues/8847)).
|
|
17
|
+
- Cloud Code Assist Gemini 3.6/3.7 Flash no longer maps user `minimal` to wire `thinkingLevel: MINIMAL` when that effort is aliased onto the `-low` SKU. The request now sends `LOW`, which those SKUs accept.
|
|
18
|
+
- Fixed SuperGrok (`xai-oauth`) Grok 4.6 hiding the thinking-level picker: the Responses effort-capable allowlist now includes `grok-4.6`, so `/model` can select the documented `low`/`medium`/`high`/`xhigh` ladder (`max` is rejected by api.x.ai).
|
|
19
|
+
- Marked CoreWeave runtime discovery as authoritative so stale bundled model ids that the endpoint no longer serves stop appearing as selectable models.
|
|
20
|
+
- ChatGPT Codex discovery that advertises only worker `-wm` SKUs now also registers the plain model route, so a configured `openai-codex/<model>` keeps resolving instead of fuzzy-falling-back to the `-wm` SKU some accounts reject.
|
|
21
|
+
- Fixed `opencode-go/muse-spark-1.2` (and `muse-spark-1.2-contributor`) failing every tool-call turn with `OpenAI completions stream closed before a finish_reason was received`. The Go gateway serves these ids only at `/zen/go/v1/responses`, but the `/zen/go/v1/models` discovery omits the `provider.npm` hint, so the resolver fell through to `openai-completions`; both ids are now pinned to `openai-responses` like `deepseek-v4-flash` ([#8957](https://github.com/can1357/oh-my-pi/issues/8957)).
|
|
22
|
+
- Fixed GitHub Copilot `grok-4.6` / `grok-4.6-1m` failing with HTTP 400 `unsupported_api_for_model` by routing them through the OpenAI Responses API (`/responses`) instead of `/chat/completions`, matching `grok-4.5`. Stale cached completion routes are invalidated on refresh ([#8807](https://github.com/can1357/oh-my-pi/issues/8807)).
|
|
23
|
+
- Fixed Cursor Grok 4.5/4.6 discovery classifying the versioned ids as non-reasoning: `GetUsableModels` ships no `thinkingDetails` and the bundled references read `reasoning: false`, so the picker hid the effort ladder. Discovery now marks `cursor-grok-<version>` ids as reasoning models (the non-reasoning `grok-code-*` ids stay out) ([#8803](https://github.com/can1357/oh-my-pi/issues/8803)).
|
|
24
|
+
- Fixed GMI Cloud (`gmi-cloud`) models resolved via `/v1/models` discovery surfacing with `null` context windows, zero pricing, and no reasoning/thinking metadata for every model except the bundled `deepseek-ai/DeepSeek-V4-Flash` seed. GMI's endpoint returns only bare `{id}` rows, so the mapper now recovers intrinsic capability metadata (context window, output limit, reasoning, thinking ladder) for resold open-weight models from the cross-provider canonical reference index — matching the SiliconFlow behavior — while never borrowing another provider's pricing ([#8890](https://github.com/can1357/oh-my-pi/issues/8890)).
|
|
25
|
+
|
|
26
|
+
## [17.3.6] - 2026-08-17
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Changed the paid xAI (XAI_API_KEY) and SuperGrok (xai-oauth) default models to grok-4.6.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Raised the GPT-5.6 Sol/Terra/Luna context window on the Codex transport (openai-codex) from 372K to 1M tokens: OpenAI enabled the 1M window for subscription Codex on 2026-08-16, but the Codex model registry still reports the stale 272,000, so discovery now floors these SKUs at 1,000,000 instead of trusting the reported value ([openai/codex#38917](https://github.com/openai/codex/issues/38917)).
|
|
35
|
+
|
|
5
36
|
## [17.3.5] - 2026-08-16
|
|
6
37
|
|
|
7
38
|
### Added
|
|
@@ -1218,15 +1218,22 @@ export type ToolCall = Message<"agent.v1.ToolCall"> & {
|
|
|
1218
1218
|
*/
|
|
1219
1219
|
value: SearchConversationsToolCall;
|
|
1220
1220
|
case: "searchConversationsToolCall";
|
|
1221
|
+
} | {
|
|
1222
|
+
/**
|
|
1223
|
+
* @generated from field: agent.v1.FetchToolCall web_fetch_tool_call = 37;
|
|
1224
|
+
*/
|
|
1225
|
+
value: FetchToolCall;
|
|
1226
|
+
case: "webFetchToolCall";
|
|
1221
1227
|
} | {
|
|
1222
1228
|
case: undefined;
|
|
1223
1229
|
value?: undefined;
|
|
1224
1230
|
};
|
|
1225
1231
|
/**
|
|
1226
1232
|
* Modern builds carry the call id on the envelope instead of inside each
|
|
1227
|
-
* variant's args.
|
|
1228
|
-
*
|
|
1229
|
-
* modelled and decode into
|
|
1233
|
+
* variant's args. Field 37 is the hosted WebFetch tool. Fields 38-53, 55,
|
|
1234
|
+
* 56, 58 (further tool variants) and 54 (hook_additional_contexts),
|
|
1235
|
+
* 59/60 (started_at_ms/completed_at_ms) are not modelled and decode into
|
|
1236
|
+
* unknown fields; do not reuse those numbers.
|
|
1230
1237
|
*
|
|
1231
1238
|
* @generated from field: optional string tool_call_id = 57;
|
|
1232
1239
|
*/
|
|
@@ -2923,6 +2930,12 @@ export type InteractionQuery = Message<"agent.v1.InteractionQuery"> & {
|
|
|
2923
2930
|
*/
|
|
2924
2931
|
value: SetupVmEnvironmentArgs;
|
|
2925
2932
|
case: "setupVmEnvironmentArgs";
|
|
2933
|
+
} | {
|
|
2934
|
+
/**
|
|
2935
|
+
* @generated from field: agent.v1.WebFetchRequestQuery web_fetch_request_query = 9;
|
|
2936
|
+
*/
|
|
2937
|
+
value: WebFetchRequestQuery;
|
|
2938
|
+
case: "webFetchRequestQuery";
|
|
2926
2939
|
} | {
|
|
2927
2940
|
case: undefined;
|
|
2928
2941
|
value?: undefined;
|
|
@@ -2986,6 +2999,12 @@ export type InteractionResponse = Message<"agent.v1.InteractionResponse"> & {
|
|
|
2986
2999
|
*/
|
|
2987
3000
|
value: SetupVmEnvironmentResult;
|
|
2988
3001
|
case: "setupVmEnvironmentResult";
|
|
3002
|
+
} | {
|
|
3003
|
+
/**
|
|
3004
|
+
* @generated from field: agent.v1.WebFetchRequestResponse web_fetch_request_response = 9;
|
|
3005
|
+
*/
|
|
3006
|
+
value: WebFetchRequestResponse;
|
|
3007
|
+
case: "webFetchRequestResponse";
|
|
2989
3008
|
} | {
|
|
2990
3009
|
case: undefined;
|
|
2991
3010
|
value?: undefined;
|
|
@@ -2996,6 +3015,72 @@ export type InteractionResponse = Message<"agent.v1.InteractionResponse"> & {
|
|
|
2996
3015
|
* Use `create(InteractionResponseSchema)` to create a new message.
|
|
2997
3016
|
*/
|
|
2998
3017
|
export declare const InteractionResponseSchema: GenMessage<InteractionResponse>;
|
|
3018
|
+
/**
|
|
3019
|
+
* @generated from message agent.v1.WebFetchRequestQuery
|
|
3020
|
+
*/
|
|
3021
|
+
export type WebFetchRequestQuery = Message<"agent.v1.WebFetchRequestQuery"> & {
|
|
3022
|
+
/**
|
|
3023
|
+
* @generated from field: agent.v1.FetchArgs args = 1;
|
|
3024
|
+
*/
|
|
3025
|
+
args?: FetchArgs | undefined;
|
|
3026
|
+
};
|
|
3027
|
+
/**
|
|
3028
|
+
* Describes the message agent.v1.WebFetchRequestQuery.
|
|
3029
|
+
* Use `create(WebFetchRequestQuerySchema)` to create a new message.
|
|
3030
|
+
*/
|
|
3031
|
+
export declare const WebFetchRequestQuerySchema: GenMessage<WebFetchRequestQuery>;
|
|
3032
|
+
/**
|
|
3033
|
+
* @generated from message agent.v1.WebFetchRequestResponse
|
|
3034
|
+
*/
|
|
3035
|
+
export type WebFetchRequestResponse = Message<"agent.v1.WebFetchRequestResponse"> & {
|
|
3036
|
+
/**
|
|
3037
|
+
* @generated from oneof agent.v1.WebFetchRequestResponse.result
|
|
3038
|
+
*/
|
|
3039
|
+
result: {
|
|
3040
|
+
/**
|
|
3041
|
+
* @generated from field: agent.v1.WebFetchRequestResponse_Approved approved = 1;
|
|
3042
|
+
*/
|
|
3043
|
+
value: WebFetchRequestResponse_Approved;
|
|
3044
|
+
case: "approved";
|
|
3045
|
+
} | {
|
|
3046
|
+
/**
|
|
3047
|
+
* @generated from field: agent.v1.WebFetchRequestResponse_Rejected rejected = 2;
|
|
3048
|
+
*/
|
|
3049
|
+
value: WebFetchRequestResponse_Rejected;
|
|
3050
|
+
case: "rejected";
|
|
3051
|
+
} | {
|
|
3052
|
+
case: undefined;
|
|
3053
|
+
value?: undefined;
|
|
3054
|
+
};
|
|
3055
|
+
};
|
|
3056
|
+
/**
|
|
3057
|
+
* Describes the message agent.v1.WebFetchRequestResponse.
|
|
3058
|
+
* Use `create(WebFetchRequestResponseSchema)` to create a new message.
|
|
3059
|
+
*/
|
|
3060
|
+
export declare const WebFetchRequestResponseSchema: GenMessage<WebFetchRequestResponse>;
|
|
3061
|
+
/**
|
|
3062
|
+
* @generated from message agent.v1.WebFetchRequestResponse_Approved
|
|
3063
|
+
*/
|
|
3064
|
+
export type WebFetchRequestResponse_Approved = Message<"agent.v1.WebFetchRequestResponse_Approved"> & {};
|
|
3065
|
+
/**
|
|
3066
|
+
* Describes the message agent.v1.WebFetchRequestResponse_Approved.
|
|
3067
|
+
* Use `create(WebFetchRequestResponse_ApprovedSchema)` to create a new message.
|
|
3068
|
+
*/
|
|
3069
|
+
export declare const WebFetchRequestResponse_ApprovedSchema: GenMessage<WebFetchRequestResponse_Approved>;
|
|
3070
|
+
/**
|
|
3071
|
+
* @generated from message agent.v1.WebFetchRequestResponse_Rejected
|
|
3072
|
+
*/
|
|
3073
|
+
export type WebFetchRequestResponse_Rejected = Message<"agent.v1.WebFetchRequestResponse_Rejected"> & {
|
|
3074
|
+
/**
|
|
3075
|
+
* @generated from field: string reason = 1;
|
|
3076
|
+
*/
|
|
3077
|
+
reason: string;
|
|
3078
|
+
};
|
|
3079
|
+
/**
|
|
3080
|
+
* Describes the message agent.v1.WebFetchRequestResponse_Rejected.
|
|
3081
|
+
* Use `create(WebFetchRequestResponse_RejectedSchema)` to create a new message.
|
|
3082
|
+
*/
|
|
3083
|
+
export declare const WebFetchRequestResponse_RejectedSchema: GenMessage<WebFetchRequestResponse_Rejected>;
|
|
2999
3084
|
/**
|
|
3000
3085
|
* @generated from message agent.v1.AskQuestionInteractionQuery
|
|
3001
3086
|
*/
|
|
@@ -30,6 +30,17 @@ export declare const isClaudeModelId: (modelId: string) => boolean;
|
|
|
30
30
|
export declare const isAnthropicNamespacedModelId: (modelId: string) => boolean;
|
|
31
31
|
/** Qwen family ids (substring match — Qwen SKUs have no stable prefix shape). */
|
|
32
32
|
export declare const isQwenModelId: (modelId: string) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Open-weight Qwen 3.8+ releases (`qwen3.8-27b`, `qwen3.8-2.4t-a95b`, GGUF
|
|
35
|
+
* names like `Qwen3.8-27B-UD-Q6_K_XL`) whose chat template steers thinking
|
|
36
|
+
* depth through a `reasoning_effort` template kwarg (`low`/`medium`/`xhigh`,
|
|
37
|
+
* template default `xhigh`; thinking itself cannot be disabled). Compared
|
|
38
|
+
* component-wise so `qwen3.10` sorts after `qwen3.8`. API-only `-max` SKUs are
|
|
39
|
+
* excluded — Dashscope drives them through OpenAI-style `reasoning_effort`
|
|
40
|
+
* with curated compat. The trailing guard rejects parameter-count lookalikes
|
|
41
|
+
* (`qwen-3.8b`) without breaking `qwen3.8-27b`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const isQwen38PlusTemplateEffortModelId: (modelId: string) => boolean;
|
|
33
44
|
/** Gemma open-weights family (`gemma-3-27b-it`, `google/gemma-4-E2B-it`, `gemma2-9b`). */
|
|
34
45
|
export declare const isGemmaModelId: (modelId: string) => boolean;
|
|
35
46
|
/** DeepSeek family by id or display name (proxies often rename the id but keep the name). */
|
|
@@ -52,7 +63,8 @@ export declare const isGrokModelId: (modelId: string) => boolean;
|
|
|
52
63
|
/**
|
|
53
64
|
* Grok SKUs that expose the wire `reasoning.effort` dial. Other Grok reasoners
|
|
54
65
|
* (e.g. `grok-build`, `grok-4.20-0309-reasoning`) think natively but reject the
|
|
55
|
-
* param, so callers must omit reasoning effort for them.
|
|
66
|
+
* param, so callers must omit reasoning effort for them. `grok-4.6` accepts
|
|
67
|
+
* `low`/`medium`/`high`/`xhigh` and 400s on `max`.
|
|
56
68
|
*/
|
|
57
69
|
export declare const isGrokReasoningEffortCapable: (modelId: string) => boolean;
|
|
58
70
|
/**
|
|
@@ -44,8 +44,12 @@ export declare function getSupportedEfforts<TApi extends Api>(model: ApiModel<TA
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function clampThinkingLevelForModel<TApi extends Api>(model: ApiModel<TApi> | undefined, requested: Effort | undefined): Effort | undefined;
|
|
46
46
|
export declare function requireSupportedEffort<TApi extends Api>(model: ApiModel<TApi>, effort: Effort): Effort;
|
|
47
|
-
/** Maps a normalized thinking effort to Google's `thinkingLevel` enum values.
|
|
48
|
-
|
|
47
|
+
/** Maps a normalized thinking effort to Google's `thinkingLevel` enum values.
|
|
48
|
+
* When a collapsed family routes `minimal` onto the same wire id as `low`
|
|
49
|
+
* (Antigravity Gemini 3.6/3.7 Flash), emit `LOW` — Cloud Code Assist rejects
|
|
50
|
+
* `MINIMAL` on those `-low` SKUs.
|
|
51
|
+
*/
|
|
52
|
+
export declare function mapEffortToGoogleThinkingLevel<TApi extends Api>(effort: Effort, model?: ApiModel<TApi>): "MINIMAL" | "LOW" | "MEDIUM" | "HIGH";
|
|
49
53
|
/**
|
|
50
54
|
* Maps a normalized thinking effort to Anthropic adaptive effort values via
|
|
51
55
|
* the model's baked `thinking.effortMap` (identity for unmapped efforts).
|
|
@@ -428,17 +428,18 @@ export declare const CATALOG_PROVIDERS: readonly [{
|
|
|
428
428
|
readonly defaultModel: "openai/gpt-oss-120b";
|
|
429
429
|
readonly envVars: readonly ["COREWEAVE_API_KEY", "WANDB_API_KEY"];
|
|
430
430
|
readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-completions", unknown>;
|
|
431
|
+
readonly dynamicModelsAuthoritative: true;
|
|
431
432
|
readonly catalogDiscovery: {
|
|
432
433
|
readonly label: "CoreWeave Serverless Inference";
|
|
433
434
|
};
|
|
434
435
|
}, {
|
|
435
436
|
readonly id: "xai";
|
|
436
|
-
readonly defaultModel: "grok-4.
|
|
437
|
+
readonly defaultModel: "grok-4.6";
|
|
437
438
|
readonly envVars: readonly ["XAI_API_KEY"];
|
|
438
439
|
readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-responses", unknown>;
|
|
439
440
|
}, {
|
|
440
441
|
readonly id: "xai-oauth";
|
|
441
|
-
readonly defaultModel: "grok-4.
|
|
442
|
+
readonly defaultModel: "grok-4.6";
|
|
442
443
|
readonly envVars: readonly ["XAI_OAUTH_TOKEN", "XAI_API_KEY"];
|
|
443
444
|
readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-responses", unknown>;
|
|
444
445
|
readonly catalogDiscovery: {
|
package/dist/types/types.d.ts
CHANGED
|
@@ -220,6 +220,16 @@ export interface OpenAICompat {
|
|
|
220
220
|
* Non-Qwen templates ignore the flag, so the auto-detection is safe.
|
|
221
221
|
*/
|
|
222
222
|
qwenPreserveThinking?: boolean;
|
|
223
|
+
/**
|
|
224
|
+
* Route the requested thinking effort onto the Qwen 3.8+ chat template's
|
|
225
|
+
* `reasoning_effort` kwarg (`low`/`medium`/`xhigh`; template default
|
|
226
|
+
* `xhigh`). Emitted inside `chat_template_kwargs` for both Qwen dialects
|
|
227
|
+
* (plus the top-level field on the `qwen` dialect, which newer llama.cpp
|
|
228
|
+
* builds map natively). Without it the qwen dialects only toggle
|
|
229
|
+
* `enable_thinking` and the template always thinks at its `xhigh` default.
|
|
230
|
+
* Default: auto-detected (Qwen 3.8+ id on a local llama.cpp-style backend).
|
|
231
|
+
*/
|
|
232
|
+
qwenTemplateReasoningEffort?: boolean;
|
|
223
233
|
/** Whether assistant tool-call messages must include non-empty content. Default: false. */
|
|
224
234
|
requiresAssistantContentForToolCalls?: boolean;
|
|
225
235
|
/** Whether the provider supports the `tool_choice` parameter. Default: true. */
|
|
@@ -558,6 +568,7 @@ export interface ResolvedOpenAISharedCompat {
|
|
|
558
568
|
allowsSyntheticReasoningContentForToolCalls: boolean;
|
|
559
569
|
replayReasoningContent: boolean;
|
|
560
570
|
qwenPreserveThinking: boolean;
|
|
571
|
+
qwenTemplateReasoningEffort: boolean;
|
|
561
572
|
requiresThinkingAsText: boolean;
|
|
562
573
|
requiresMistralToolIds: boolean;
|
|
563
574
|
requiresToolResultName: boolean;
|
|
@@ -595,7 +606,7 @@ export interface ResolvedOpenAISharedCompat {
|
|
|
595
606
|
* `buildModel`; request handlers read fields and never detect, resolve, or
|
|
596
607
|
* allocate.
|
|
597
608
|
*/
|
|
598
|
-
export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsSamplingParams" | "supportsPenaltyAndStopParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "qwenPreserveThinking" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "whenThinking">> & {
|
|
609
|
+
export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsSamplingParams" | "supportsPenaltyAndStopParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "qwenPreserveThinking" | "qwenTemplateReasoningEffort" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "whenThinking">> & {
|
|
599
610
|
vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
|
|
600
611
|
extraBody?: OpenAICompat["extraBody"];
|
|
601
612
|
cacheControlFormat?: OpenAICompat["cacheControlFormat"];
|
|
@@ -58,6 +58,8 @@ export declare const ANTIGRAVITY_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
|
58
58
|
/** `google-gemini-cli` Gemini families on the official CLI's level transport. */
|
|
59
59
|
export declare const GEMINI_CLI_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
60
60
|
export declare const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
61
|
+
/** `cursor` Grok families: per-effort siblings collapsed per service-tier lane. */
|
|
62
|
+
export declare const CURSOR_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
61
63
|
/** Provider id → hand collapse table. The CCA providers diverge on thinking transport. */
|
|
62
64
|
export declare const VARIANT_COLLAPSE_TABLES: Readonly<Record<string, VariantCollapseTable>>;
|
|
63
65
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-catalog",
|
|
4
|
-
"version": "17.3.
|
|
4
|
+
"version": "17.3.8",
|
|
5
5
|
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@bufbuild/protobuf": "^2.12.1",
|
|
38
|
-
"@oh-my-pi/omptype": "17.3.
|
|
39
|
-
"@oh-my-pi/pi-utils": "17.3.
|
|
38
|
+
"@oh-my-pi/omptype": "17.3.8",
|
|
39
|
+
"@oh-my-pi/pi-utils": "17.3.8"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "17.3.
|
|
42
|
+
"@oh-my-pi/pi-ai": "17.3.8",
|
|
43
43
|
"@types/bun": "^1.3.14"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
package/src/compat/openai.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
isKimiModelId,
|
|
23
23
|
isMimoModelIdOrName,
|
|
24
24
|
isOpenAISamplingRestrictedModelId,
|
|
25
|
+
isQwen38PlusTemplateEffortModelId,
|
|
25
26
|
isQwenModelId,
|
|
26
27
|
} from "../identity/family";
|
|
27
28
|
import type {
|
|
@@ -464,7 +465,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
|
|
464
465
|
? "zai"
|
|
465
466
|
: isOpenRouter
|
|
466
467
|
? "openrouter"
|
|
467
|
-
: isQwen && isNvidiaNim
|
|
468
|
+
: isQwen && (isNvidiaNim || provider === "vllm")
|
|
468
469
|
? "qwen-chat-template"
|
|
469
470
|
: isQwen && isFireworks
|
|
470
471
|
? "openai"
|
|
@@ -587,6 +588,18 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
|
|
587
588
|
// parameter, so the flag stays a no-op outside the Qwen path.
|
|
588
589
|
qwenPreserveThinking:
|
|
589
590
|
(thinkingFormat === "qwen" || thinkingFormat === "qwen-chat-template") && isLocalOpenAICompatBackend,
|
|
591
|
+
// Qwen 3.8+ templates steer thinking depth via the `reasoning_effort`
|
|
592
|
+
// template kwarg (low/medium/xhigh, default xhigh); without routing the
|
|
593
|
+
// requested effort there, the enable_thinking toggle alone leaves the
|
|
594
|
+
// model at xhigh no matter what the user selects.
|
|
595
|
+
// Local-only like `qwenPreserveThinking`: first-party Qwen APIs
|
|
596
|
+
// (Dashscope, Qwen Portal) drive effort through their own OpenAI-style
|
|
597
|
+
// dialect, and local Ollama keeps its native effort vocabulary.
|
|
598
|
+
qwenTemplateReasoningEffort:
|
|
599
|
+
(thinkingFormat === "qwen" || thinkingFormat === "qwen-chat-template") &&
|
|
600
|
+
isLocalOpenAICompatBackend &&
|
|
601
|
+
provider !== "ollama" &&
|
|
602
|
+
isQwen38PlusTemplateEffortModelId(spec.id),
|
|
590
603
|
requiresAssistantContentForToolCalls: isKimiModel || isDirectDeepseekReasoning,
|
|
591
604
|
cacheControlFormat: isOpenRouter && spec.id.startsWith("anthropic/") ? "anthropic" : undefined,
|
|
592
605
|
supportsPromptCacheBreakpoints,
|
|
@@ -761,6 +774,7 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
|
|
|
761
774
|
// Responses-only; the Qwen `preserve_thinking` template knob lives on
|
|
762
775
|
// the chat-completions wire shape, never on Responses.
|
|
763
776
|
qwenPreserveThinking: false,
|
|
777
|
+
qwenTemplateReasoningEffort: false,
|
|
764
778
|
requiresThinkingAsText: false,
|
|
765
779
|
requiresMistralToolIds: false,
|
|
766
780
|
requiresToolResultName: false,
|
|
@@ -807,6 +821,18 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
|
|
|
807
821
|
if (spec.compat?.omitReasoningEffort === undefined && !compat.supportsReasoningEffort) {
|
|
808
822
|
compat.omitReasoningEffort = true;
|
|
809
823
|
}
|
|
824
|
+
// xai-oauth cache/discovery rows written before a SKU joined the
|
|
825
|
+
// effort-capable allowlist still carry omitReasoningEffort: true. The
|
|
826
|
+
// allowlist is the live wire contract; do not let that stale flag hide
|
|
827
|
+
// the picker or strip reasoning.effort.
|
|
828
|
+
if (
|
|
829
|
+
spec.provider === "xai-oauth" &&
|
|
830
|
+
isGrokReasoningEffortCapable(id) &&
|
|
831
|
+
spec.compat?.supportsReasoningEffort !== false
|
|
832
|
+
) {
|
|
833
|
+
compat.supportsReasoningEffort = true;
|
|
834
|
+
compat.omitReasoningEffort = false;
|
|
835
|
+
}
|
|
810
836
|
return compat;
|
|
811
837
|
}
|
|
812
838
|
|
package/src/discovery/codex.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type } from "@oh-my-pi/omptype";
|
|
2
2
|
import { parseKnownModel, semverEqual } from "../identity/classify";
|
|
3
|
+
import { getBundledModels } from "../models";
|
|
3
4
|
import { resolveOpenAIDaybreakStandardCost } from "../openai-pricing";
|
|
4
5
|
import type { FetchImpl, ModelSpec } from "../types";
|
|
5
6
|
import { discoveryFetch } from "../utils";
|
|
@@ -9,13 +10,42 @@ const DEFAULT_MODEL_LIST_PATHS = ["/codex/models", "/models"] as const;
|
|
|
9
10
|
const DEFAULT_CONTEXT_WINDOW = 272_000;
|
|
10
11
|
const DEFAULT_MAX_TOKENS = 128_000;
|
|
11
12
|
/**
|
|
12
|
-
* GPT-5.6
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* declares context_window = max_context_window = 372000 (#5705). Used as the
|
|
16
|
-
* fallback only when upstream reports no value.
|
|
13
|
+
* Fallback for GPT-5.6-family SKUs when upstream omits `context_window`: the
|
|
14
|
+
* generic {@link DEFAULT_CONTEXT_WINDOW} (272000) understates the registry's
|
|
15
|
+
* former 372000 hard capacity (#5705).
|
|
17
16
|
*/
|
|
18
17
|
const GPT_5_6_CONTEXT_WINDOW = 372_000;
|
|
18
|
+
/**
|
|
19
|
+
* OpenAI enabled a 1M-token window for subscription Codex on GPT-5.6
|
|
20
|
+
* luna/sol/terra (2026-08-16), but the Codex model registry still reports the
|
|
21
|
+
* stale 272000 — so the reported value must be floored, not just defaulted
|
|
22
|
+
* (openai/codex#38917; Codex CLI override `model_context_window = 1000000`).
|
|
23
|
+
*/
|
|
24
|
+
const GPT_5_6_1M_CONTEXT_WINDOW = 1_000_000;
|
|
25
|
+
const CODEX_GPT_5_6_1M_SLUGS: ReadonlySet<string> = new Set(["gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"]);
|
|
26
|
+
/**
|
|
27
|
+
* Codex advertises worker-mode SKUs under a `-wm` suffix (`gpt-5.6-luna-wm`).
|
|
28
|
+
*
|
|
29
|
+
* Those rows route through the same Codex backend as their plain SKU, but an
|
|
30
|
+
* authoritative discovery list that only advertises the `-wm` slug prunes the
|
|
31
|
+
* bundled plain model, leaving a configured `openai-codex/gpt-5.6-luna`
|
|
32
|
+
* unresolvable except via fuzzy fallback onto the `-wm` row — which this user's
|
|
33
|
+
* ChatGPT account rejects. The compatibility rule, scoped to Codex discovery:
|
|
34
|
+
* a `-wm` slug whose plain counterpart exists in the bundled Codex catalog is
|
|
35
|
+
* ALSO registered under its plain id. Both listings derive their base-model
|
|
36
|
+
* metadata (1M-window floor, daybreak pricing, context fallback) from the
|
|
37
|
+
* canonical plain slug — the suffix is a routing variant, not a different
|
|
38
|
+
* model, so the `-wm` row no longer keeps stale backend-parsed capability
|
|
39
|
+
* values while its plain listing is enriched.
|
|
40
|
+
*
|
|
41
|
+
* Deliberate boundary: the "safe" gate is the bundled Codex catalog. A `-wm`
|
|
42
|
+
* slug whose plain counterpart is only a user-local models.yml entry (not
|
|
43
|
+
* bundled) stays verbatim — authoritative discovery for genuinely distinct
|
|
44
|
+
* `-wm` SKUs is preserved, and a hidden plain backend entry can be re-surfaced
|
|
45
|
+
* through its advertised `-wm` row because the configured plain slug must
|
|
46
|
+
* resolve.
|
|
47
|
+
*/
|
|
48
|
+
const CODEX_WORKER_SUFFIX = "-wm";
|
|
19
49
|
const CODEX_REMOTE_COMPACTION = {
|
|
20
50
|
enabled: true,
|
|
21
51
|
api: "openai-codex-responses",
|
|
@@ -188,11 +218,30 @@ function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"ope
|
|
|
188
218
|
}
|
|
189
219
|
|
|
190
220
|
const entries = parsedResponse.models ?? parsedResponse.data ?? [];
|
|
191
|
-
const
|
|
221
|
+
const parsedEntries: ParsedCodexModelEntry[] = [];
|
|
192
222
|
for (const entry of entries) {
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
|
|
223
|
+
const parsed = parseCodexModelEntry(entry);
|
|
224
|
+
if (parsed) {
|
|
225
|
+
parsedEntries.push(parsed);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// A worker `-wm` slug gets an extra plain-id route only when the bundled
|
|
230
|
+
// catalog ships the plain SKU (the "safe" precondition); the backend's own
|
|
231
|
+
// plain slug wins over any synthesized clone, and unknown `-wm` SKUs stay
|
|
232
|
+
// verbatim. Both listings of a safe `-wm` model carry the same base-model
|
|
233
|
+
// metadata (context-window floor, daybreak pricing) derived from the
|
|
234
|
+
// canonical plain slug — the suffix is a routing variant, not a different
|
|
235
|
+
// model.
|
|
236
|
+
const advertisedSlugs = new Set(parsedEntries.map(parsed => parsed.slug));
|
|
237
|
+
const bundledCodexModelIds = getBundledCodexModelIds();
|
|
238
|
+
const normalized: NormalizedCodexModel[] = [];
|
|
239
|
+
for (const parsed of parsedEntries) {
|
|
240
|
+
const canonicalSlug = plainCounterpartForWorkerSlug(parsed.slug, bundledCodexModelIds) ?? parsed.slug;
|
|
241
|
+
normalized.push(buildNormalizedCodexModel(parsed, parsed.slug, canonicalSlug, baseUrl));
|
|
242
|
+
const plainSlug = canonicalSlug !== parsed.slug ? canonicalSlug : null;
|
|
243
|
+
if (plainSlug && !advertisedSlugs.has(plainSlug)) {
|
|
244
|
+
normalized.push(buildNormalizedCodexModel(parsed, plainSlug, canonicalSlug, baseUrl));
|
|
196
245
|
}
|
|
197
246
|
}
|
|
198
247
|
|
|
@@ -206,7 +255,37 @@ function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"ope
|
|
|
206
255
|
return normalized.map(item => item.model);
|
|
207
256
|
}
|
|
208
257
|
|
|
209
|
-
|
|
258
|
+
/** Ids of the bundled Codex catalog, consulted once per discovery run. */
|
|
259
|
+
function getBundledCodexModelIds(): ReadonlySet<string> {
|
|
260
|
+
const ids = new Set(getBundledModels("openai-codex").map(model => model.id));
|
|
261
|
+
return ids;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Map a Codex worker `-wm` slug to its plain counterpart when the bundled
|
|
266
|
+
* catalog registers that plain SKU. Returns `null` for non-worker slugs and
|
|
267
|
+
* for `-wm` slugs without a safe plain counterpart.
|
|
268
|
+
*/
|
|
269
|
+
function plainCounterpartForWorkerSlug(slug: string, bundledCodexModelIds: ReadonlySet<string>): string | null {
|
|
270
|
+
if (!slug.endsWith(CODEX_WORKER_SUFFIX)) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const plain = slug.slice(0, -CODEX_WORKER_SUFFIX.length);
|
|
274
|
+
return plain.length > 0 && bundledCodexModelIds.has(plain) ? plain : null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
interface ParsedCodexModelEntry {
|
|
278
|
+
slug: string;
|
|
279
|
+
name: string;
|
|
280
|
+
contextWindow: number | null;
|
|
281
|
+
reasoning: boolean;
|
|
282
|
+
input: ("text" | "image")[];
|
|
283
|
+
preferWebsockets: boolean;
|
|
284
|
+
useResponsesLite: boolean;
|
|
285
|
+
priority: number;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
|
|
210
289
|
const parsedEntry = codexModelEntrySchema(entry);
|
|
211
290
|
if (parsedEntry instanceof type.errors) {
|
|
212
291
|
return null;
|
|
@@ -223,40 +302,65 @@ function normalizeCodexModelEntry(entry: unknown, baseUrl: string): NormalizedCo
|
|
|
223
302
|
return null;
|
|
224
303
|
}
|
|
225
304
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
305
|
+
return {
|
|
306
|
+
slug,
|
|
307
|
+
name: toNonEmptyString(payload.display_name) ?? slug,
|
|
308
|
+
contextWindow: toPositiveInt(payload.context_window),
|
|
309
|
+
reasoning: supportsReasoning(payload.default_reasoning_level, payload.supported_reasoning_levels),
|
|
310
|
+
input: normalizeInputModalities(payload.input_modalities),
|
|
311
|
+
preferWebsockets: toBoolean(payload.prefer_websockets) === true,
|
|
312
|
+
useResponsesLite: toBoolean(payload.use_responses_lite) === true,
|
|
313
|
+
priority: toFiniteNumber(payload.priority) ?? Number.MAX_SAFE_INTEGER,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Build a normalized Codex model spec. `slug` is the registered id (either the
|
|
319
|
+
* advertised slug or a synthesized plain counterpart); `canonicalSlug` names
|
|
320
|
+
* the model's bundled SKU (`slug` itself for plain/unknown rows, the plain
|
|
321
|
+
* counterpart for a safe `-wm` row) and owns the base-model metadata derivation
|
|
322
|
+
* so both listings of a model report the same context window and pricing.
|
|
323
|
+
*/
|
|
324
|
+
function buildNormalizedCodexModel(
|
|
325
|
+
parsed: ParsedCodexModelEntry,
|
|
326
|
+
slug: string,
|
|
327
|
+
canonicalSlug: string,
|
|
328
|
+
baseUrl: string,
|
|
329
|
+
): NormalizedCodexModel {
|
|
330
|
+
// Codex discovery historically omitted `context_window` for GPT-5.6-family
|
|
331
|
+
// SKUs (#5705); luna/sol/terra additionally floor the reported value because
|
|
332
|
+
// the registry still declares the pre-1M 272000 window. Keyed on the
|
|
333
|
+
// canonical slug so a safe `gpt-5.6-luna-wm` row gets the same floor as its
|
|
334
|
+
// plain listing.
|
|
335
|
+
const parsedKnown = parseKnownModel(canonicalSlug);
|
|
230
336
|
const fallbackContextWindow =
|
|
231
|
-
|
|
337
|
+
parsedKnown.family === "openai" && semverEqual(parsedKnown.version, "5.6")
|
|
232
338
|
? GPT_5_6_CONTEXT_WINDOW
|
|
233
339
|
: DEFAULT_CONTEXT_WINDOW;
|
|
234
|
-
const
|
|
340
|
+
const reportedContextWindow = parsed.contextWindow ?? fallbackContextWindow;
|
|
341
|
+
const contextWindow = CODEX_GPT_5_6_1M_SLUGS.has(canonicalSlug)
|
|
342
|
+
? Math.max(reportedContextWindow, GPT_5_6_1M_CONTEXT_WINDOW)
|
|
343
|
+
: reportedContextWindow;
|
|
235
344
|
const maxTokens = Math.min(DEFAULT_MAX_TOKENS, contextWindow);
|
|
236
|
-
const
|
|
237
|
-
const input = normalizeInputModalities(payload.input_modalities);
|
|
238
|
-
const preferWebsockets = toBoolean(payload.prefer_websockets) === true;
|
|
239
|
-
const useResponsesLite = toBoolean(payload.use_responses_lite) === true;
|
|
240
|
-
const priority = toFiniteNumber(payload.priority) ?? Number.MAX_SAFE_INTEGER;
|
|
241
|
-
const daybreakCost = resolveOpenAIDaybreakStandardCost(slug);
|
|
345
|
+
const daybreakCost = resolveOpenAIDaybreakStandardCost(canonicalSlug);
|
|
242
346
|
|
|
243
347
|
return {
|
|
244
|
-
priority,
|
|
348
|
+
priority: parsed.priority,
|
|
245
349
|
model: {
|
|
246
350
|
id: slug,
|
|
247
|
-
name,
|
|
351
|
+
name: parsed.name,
|
|
248
352
|
api: "openai-codex-responses",
|
|
249
353
|
provider: "openai-codex",
|
|
250
354
|
baseUrl,
|
|
251
|
-
reasoning,
|
|
252
|
-
input,
|
|
355
|
+
reasoning: parsed.reasoning,
|
|
356
|
+
input: parsed.input,
|
|
253
357
|
cost: daybreakCost ? { ...daybreakCost } : { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
254
358
|
remoteCompaction: CODEX_REMOTE_COMPACTION,
|
|
255
359
|
contextWindow,
|
|
256
360
|
maxTokens,
|
|
257
|
-
...(preferWebsockets ? { preferWebsockets: true } : {}),
|
|
258
|
-
...(useResponsesLite ? { useResponsesLite: true } : {}),
|
|
259
|
-
...(priority !== Number.MAX_SAFE_INTEGER ? { priority } : {}),
|
|
361
|
+
...(parsed.preferWebsockets ? { preferWebsockets: true } : {}),
|
|
362
|
+
...(parsed.useResponsesLite ? { useResponsesLite: true } : {}),
|
|
363
|
+
...(parsed.priority !== Number.MAX_SAFE_INTEGER ? { priority: parsed.priority } : {}),
|
|
260
364
|
},
|
|
261
365
|
};
|
|
262
366
|
}
|