dsh-codex-subscription 2.2.6 → 2.2.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/README.en.md CHANGED
@@ -32,6 +32,7 @@ Subscription access, model controls, quota management, and image creation in one
32
32
  | --- | --- | --- |
33
33
  | **Subscription sign-in** | Sign in to ChatGPT and use Codex subscription models without an API key | Normal chat needs no Codex CLI |
34
34
  | **Model catalog sync** | Read available models and reasoning levels from your account, with manual refresh; models marked for retirement show their retirement date next to the model name, without automatically switching your selection | New models appear when your account offers them |
35
+ | **Model list** | Show or hide individual models in the composer picker under Models & runtime | Display only; selected models and requests still work |
35
36
  | **Reasoning levels** | Choose supported reasoning effort in the composer | Levels vary by model |
36
37
  | **Fast mode** | Switch supported models between Standard and Fast, with a lightning indicator | Standard by default; Fast increases usage |
37
38
  | **Response verbosity** | Request short, medium, or detailed responses | Follows the model default unless changed |
package/README.md CHANGED
@@ -32,6 +32,7 @@
32
32
  | --- | --- | --- |
33
33
  | **订阅登录** | 直接登录 ChatGPT,使用 Codex 订阅模型,无需 API Key | 普通聊天无需 Codex CLI |
34
34
  | **模型目录同步** | 自动读取账号可用模型与推理档位,支持手动刷新;目录标注退役的模型会在模型名称旁显示退役日期,不会自动切换你的选择 | 新型号以账号目录开放情况为准 |
35
+ | **模型列表** | 在“模型与运行”中逐个显示或隐藏输入框中的模型 | 仅影响列表显示;已选模型和请求不受影响 |
35
36
  | **推理等级** | 在输入框选择模型支持的推理档位 | 档位随模型变化 |
36
37
  | **高速模式** | 在支持的模型上切换标准 / Fast,输入框显示闪电标识 | 默认标准;Fast 会增加用量消耗 |
37
38
  | **输出详略** | 选择简短、适中或详细的回答偏好 | 默认跟随模型 |
package/lib/client.js CHANGED
@@ -4755,7 +4755,7 @@ window.__ModuleLoader__.load({
4755
4755
  streamIdleTimeout_10: "10 分钟",
4756
4756
  streamIdleTimeout_20: "20 分钟",
4757
4757
  streamIdleTimeout_30: "30 分钟",
4758
- subagentBackendHint: "Codex 复用订阅登录和工作区权限;默认继承当前订阅模型。开启 DSH 子任务模型选择并新建会话后,可在聊天中指定允许的订阅模型与推理档位;非订阅会话需指定。共享上下文子任务仍用 DSH。",
4758
+ subagentBackendHint: "Codex 复用订阅登录和工作区权限;默认继承当前订阅模型。在“插件 → 子智能体”开启模型选择并勾选允许的模型后新建会话,可在聊天中指定允许的订阅模型与推理档位;非订阅会话需指定。共享上下文子任务仍用 DSH。",
4759
4759
  subagentBackendUnavailable: "当前宿主缺少子代理服务,请更新 DSH。",
4760
4760
  subagentRuntimeMissing: "尚未准备 Codex 子任务运行时,仅影响此选项。普通订阅对话无需安装。",
4761
4761
  subagentRuntimePrepare: "准备方法(含离线说明)",
@@ -4930,6 +4930,8 @@ window.__ModuleLoader__.load({
4930
4930
  catalogRefresh: "刷新模型",
4931
4931
  catalogOnline: "已加载账户在线模型目录",
4932
4932
  catalogFallback: "当前使用内置模型目录;登录后可刷新账户可用模型",
4933
+ pickerModelsTitle: "输入框模型列表",
4934
+ pickerModelsHint: "只控制输入框中的模型列表;不会影响已选模型或模型请求。新模型默认显示。",
4933
4935
  searchMode: "订阅搜索模式",
4934
4936
  searchMode_live: "实时",
4935
4937
  searchMode_cached: "缓存(实验)",
@@ -5147,6 +5149,7 @@ window.__ModuleLoader__.load({
5147
5149
  selectModel: "选择模型",
5148
5150
  modelsLoading: "正在读取模型…",
5149
5151
  modelsEmpty: "没有可用模型。",
5152
+ modelsHidden: "所有模型都已从列表中隐藏;可在设置中重新显示。",
5150
5153
  effortsEmpty: "当前模型未提供推理等级。",
5151
5154
  modelRetry: "重试",
5152
5155
  modelDirectoryFailed: "模型目录加载失败,请重试。",
@@ -5303,7 +5306,7 @@ window.__ModuleLoader__.load({
5303
5306
  streamIdleTimeout_10: "10 min",
5304
5307
  streamIdleTimeout_20: "20 min",
5305
5308
  streamIdleTimeout_30: "30 min",
5306
- subagentBackendHint: "Codex reuses subscription login and workspace permissions, inheriting the current subscription model by default. Enable DSH subtask model selection and start a new session to request an allowed subscription model and reasoning effort in chat; other model sessions require an explicit selection. Shared-context subtasks stay in DSH.",
5309
+ subagentBackendHint: "Codex reuses subscription login and workspace permissions, inheriting the current subscription model by default. Turn on model selection under Plugins → Subagent, tick the allowed models and start a new session to request an allowed subscription model and reasoning effort in chat; other model sessions require an explicit selection. Shared-context subtasks stay in DSH.",
5307
5310
  subagentBackendUnavailable: "Subagent services are unavailable. Update DSH to use this option.",
5308
5311
  subagentRuntimeMissing: "Codex subtask runtime is not prepared. Subscription chat does not require it.",
5309
5312
  subagentRuntimePrepare: "Setup and offline instructions",
@@ -5478,6 +5481,8 @@ window.__ModuleLoader__.load({
5478
5481
  catalogRefresh: "Refresh models",
5479
5482
  catalogOnline: "Account model catalog loaded",
5480
5483
  catalogFallback: "Using the built-in catalog. Sign in to refresh account models.",
5484
+ pickerModelsTitle: "Models in picker",
5485
+ pickerModelsHint: "Only controls the composer model list. Selected models and requests still work. New models appear by default.",
5481
5486
  searchMode: "Subscription search mode",
5482
5487
  searchMode_live: "Live",
5483
5488
  searchMode_cached: "Cached (experimental)",
@@ -5695,6 +5700,7 @@ window.__ModuleLoader__.load({
5695
5700
  selectModel: "Select model",
5696
5701
  modelsLoading: "Loading models…",
5697
5702
  modelsEmpty: "No models available.",
5703
+ modelsHidden: "All models are hidden from this list. Show one in settings.",
5698
5704
  effortsEmpty: "This model provides no reasoning effort levels.",
5699
5705
  modelRetry: "Retry",
5700
5706
  modelDirectoryFailed: "Could not load the model directory. Try again.",
@@ -7021,6 +7027,7 @@ window.__ModuleLoader__.load({
7021
7027
  limitId: limit.id
7022
7028
  }))).filter((window) => Number.isFinite(window.remainingPercent) && window.remainingPercent >= 0 && window.remainingPercent <= (mode === "custom" ? readCapabilitySettings(thresholds)[window.windowSeconds > 0 && window.windowSeconds <= 6 * 3600 ? "quotaShortThreshold" : "quotaLongThreshold"] : mode === "early" && window.windowSeconds > 0 && window.windowSeconds <= 6 * 3600 ? 50 : 20) && (!Number.isFinite(window.resetsAt) || window.resetsAt * 1e3 > now)).sort((a, b) => a.remainingPercent - b.remainingPercent)[0];
7023
7029
  }
7030
+ const DISABLED_MODELS_FIELD = "disabledModels";
7024
7031
  const AUTO_QUOTA_RETRY_FIELD = "autoQuotaRetry";
7025
7032
  const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
7026
7033
  const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
@@ -7086,6 +7093,7 @@ window.__ModuleLoader__.load({
7086
7093
  "gpt-6-astra": 272e3
7087
7094
  });
7088
7095
  const normalizeAutoQuotaRetry = (value) => typeof value === "boolean" ? value : false;
7096
+ const normalizeDisabledModels = (value) => Array.isArray(value) ? [...new Set(value.filter((id) => typeof id === "string" && /^[a-z][a-z0-9._-]{0,79}$/u.test(id)))].slice(0, 100) : [];
7089
7097
  const normalizeSearchProvider = (value) => [
7090
7098
  "auto",
7091
7099
  "dsh",
@@ -7136,6 +7144,7 @@ window.__ModuleLoader__.load({
7136
7144
  let failedPatch;
7137
7145
  let generation = 0;
7138
7146
  let contextModels = [];
7147
+ let availableModels = [];
7139
7148
  let verbosityModels = [];
7140
7149
  let fastModels;
7141
7150
  let catalogStatus;
@@ -7178,6 +7187,8 @@ window.__ModuleLoader__.load({
7178
7187
  ...Object.fromEntries(contextModels.map((model) => [model.key, clampModelContext(capabilities["customContextModels"][model.key] ?? value?.[CUSTOM_CONTEXT_MODEL_FIELDS[model.key]], model.maximum, model.default ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[model.key])]))
7179
7188
  },
7180
7189
  contextModels,
7190
+ availableModels,
7191
+ disabledModels: normalizeDisabledModels(value?.[DISABLED_MODELS_FIELD]),
7181
7192
  verbosityModels,
7182
7193
  fastModels,
7183
7194
  catalogStatus,
@@ -7204,6 +7215,7 @@ window.__ModuleLoader__.load({
7204
7215
  subagentRuntimeInstalled = value?.subagentRuntimeInstalled === true;
7205
7216
  if (!modelRefreshStarted) {
7206
7217
  contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
7218
+ availableModels = Array.isArray(value?.availableModels) ? value.availableModels : [];
7207
7219
  verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
7208
7220
  fastModels = Array.isArray(value?.fastModels) ? value.fastModels : void 0;
7209
7221
  catalogStatus = value?.catalogStatus;
@@ -7212,6 +7224,7 @@ window.__ModuleLoader__.load({
7212
7224
  fallback = {
7213
7225
  status: "ready",
7214
7226
  value: {
7227
+ [DISABLED_MODELS_FIELD]: normalizeDisabledModels(value?.[DISABLED_MODELS_FIELD]),
7215
7228
  [AUTO_QUOTA_RETRY_FIELD]: normalizeAutoQuotaRetry(value?.[AUTO_QUOTA_RETRY_FIELD]),
7216
7229
  connectionMode: value?.connectionMode === "websocket" ? "websocket" : "sse",
7217
7230
  compactionMode: value?.compactionMode === "cloud" ? "cloud" : "dsh",
@@ -7246,6 +7259,7 @@ window.__ModuleLoader__.load({
7246
7259
  if (nativeSnapshot().status === "ready") {
7247
7260
  if (!modelRefreshStarted) {
7248
7261
  contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
7262
+ availableModels = Array.isArray(value?.availableModels) ? value.availableModels : [];
7249
7263
  verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
7250
7264
  fastModels = Array.isArray(value?.fastModels) ? value.fastModels : void 0;
7251
7265
  catalogStatus = value?.catalogStatus;
@@ -7268,12 +7282,14 @@ window.__ModuleLoader__.load({
7268
7282
  const value = unwrap(await rpc.call(CHANNEL, "preferences/models", {}));
7269
7283
  if (disposed || current !== modelRefreshGeneration) return false;
7270
7284
  const nextContextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
7285
+ const nextAvailableModels = Array.isArray(value?.availableModels) ? value.availableModels : [];
7271
7286
  const nextVerbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
7272
7287
  const nextFastModels = Array.isArray(value?.fastModels) ? value.fastModels : void 0;
7273
7288
  catalogStatus = value?.catalogStatus;
7274
- const changed = !sameModels(contextModels, nextContextModels) || !sameModels(verbosityModels, nextVerbosityModels) || JSON.stringify(fastModels) !== JSON.stringify(nextFastModels);
7289
+ const changed = !sameModels(contextModels, nextContextModels) || !sameModels(availableModels, nextAvailableModels) || !sameModels(verbosityModels, nextVerbosityModels) || JSON.stringify(fastModels) !== JSON.stringify(nextFastModels);
7275
7290
  if (changed) {
7276
7291
  contextModels = nextContextModels;
7292
+ availableModels = nextAvailableModels;
7277
7293
  verbosityModels = nextVerbosityModels;
7278
7294
  fastModels = nextFastModels;
7279
7295
  publish();
@@ -7636,7 +7652,7 @@ window.__ModuleLoader__.load({
7636
7652
  }
7637
7653
  //#endregion
7638
7654
  //#region src/version.js
7639
- const PACKAGE_VERSION = "2.2.6";
7655
+ const PACKAGE_VERSION = "2.2.8";
7640
7656
  //#endregion
7641
7657
  //#region src/client-recovery.js
7642
7658
  async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
@@ -7901,6 +7917,13 @@ window.__ModuleLoader__.load({
7901
7917
  ...model.reasoning?.defaultEffort === void 0 ? {} : { reasoningEffort: model.reasoning.defaultEffort }
7902
7918
  }
7903
7919
  }))), [state.groups]);
7920
+ const visibleGroups = (0, react.useMemo)(() => {
7921
+ const hidden = new Set(preferenceSnapshot.disabledModels);
7922
+ return state.groups.map((group) => group.id === "openai-codex" ? {
7923
+ ...group,
7924
+ models: group.models.filter((model) => !hidden.has(model.id))
7925
+ } : group).filter((group) => group.models.length > 0);
7926
+ }, [state.groups, preferenceSnapshot.disabledModels]);
7904
7927
  const currentChoice = choices.find((choice) => choice.selection.provider === state.current?.provider && choice.selection.model === state.current?.model);
7905
7928
  const reasoning = currentChoice?.model.reasoning;
7906
7929
  const effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
@@ -8072,7 +8095,7 @@ window.__ModuleLoader__.load({
8072
8095
  }, failure.id)),
8073
8096
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8074
8097
  className: "codexModelSelectGroups scrollable",
8075
- children: state.groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8098
+ children: visibleGroups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8076
8099
  className: "codexModelSelectGroup",
8077
8100
  role: "group",
8078
8101
  "aria-labelledby": `${id}-${group.id}`,
@@ -8093,9 +8116,9 @@ window.__ModuleLoader__.load({
8093
8116
  }))]
8094
8117
  }, group.id))
8095
8118
  }),
8096
- state.status === "ready" && choices.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8119
+ state.status === "ready" && visibleGroups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8097
8120
  className: "codexModelSelectEmpty",
8098
- children: t("modelsEmpty")
8121
+ children: t(choices.length === 0 ? "modelsEmpty" : "modelsHidden")
8099
8122
  }) : null
8100
8123
  ]
8101
8124
  });
@@ -8875,6 +8898,49 @@ window.__ModuleLoader__.load({
8875
8898
  })]
8876
8899
  });
8877
8900
  }
8901
+ /** Show every account model while saving which entries the composer picker hides. */
8902
+ function PickerModelsPreference({ preference, t }) {
8903
+ const snapshot = usePreferenceSnapshot(preference);
8904
+ const writable = snapshot.status === "ready" && snapshot.writable === true;
8905
+ const hidden = snapshot.disabledModels;
8906
+ const toggle = (modelId) => {
8907
+ const next = hidden.includes(modelId) ? hidden.filter((id) => id !== modelId) : [...hidden, modelId];
8908
+ preference.set({ [DISABLED_MODELS_FIELD]: next });
8909
+ };
8910
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8911
+ className: "codexSubscriptionContext",
8912
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8913
+ className: "codexSubscriptionContextCopy",
8914
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8915
+ className: "codexSubscriptionPreferenceLabel",
8916
+ children: t("pickerModelsTitle")
8917
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8918
+ className: "codexSubscriptionContextHint",
8919
+ children: t("pickerModelsHint")
8920
+ })]
8921
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8922
+ className: "codexSubscriptionContextModels",
8923
+ role: "group",
8924
+ "aria-label": t("pickerModelsTitle"),
8925
+ children: snapshot.availableModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8926
+ className: "codexSubscriptionContextModel",
8927
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8928
+ className: "codexSubscriptionContextModelCopy",
8929
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.name })
8930
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8931
+ className: "codexSubscriptionSwitch",
8932
+ type: "button",
8933
+ role: "switch",
8934
+ "aria-label": model.name,
8935
+ "aria-checked": !hidden.includes(model.id),
8936
+ disabled: !writable,
8937
+ onClick: () => toggle(model.id),
8938
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "codexSubscriptionSwitchKnob" })
8939
+ })]
8940
+ }, model.id))
8941
+ })]
8942
+ });
8943
+ }
8878
8944
  function ContextWindowPreference({ preference, t }) {
8879
8945
  const snapshot = usePreferenceSnapshot(preference);
8880
8946
  const writable = snapshot.status === "ready" && snapshot.writable === true;
@@ -9050,6 +9116,11 @@ window.__ModuleLoader__.load({
9050
9116
  t
9051
9117
  }),
9052
9118
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "codexSubscriptionDivider" }),
9119
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerModelsPreference, {
9120
+ preference,
9121
+ t
9122
+ }),
9123
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "codexSubscriptionDivider" }),
9053
9124
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextWindowPreference, {
9054
9125
  preference,
9055
9126
  t
package/lib/index.js CHANGED
@@ -193,6 +193,7 @@ function capabilityPatch(payload) {
193
193
  //#endregion
194
194
  //#region src/settings-contract.js
195
195
  const SETTINGS_NAMESPACE = "codex-subscription";
196
+ const DISABLED_MODELS_FIELD = "disabledModels";
196
197
  const AUTO_QUOTA_RETRY_FIELD = "autoQuotaRetry";
197
198
  const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
198
199
  const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
@@ -258,6 +259,7 @@ const CUSTOM_CONTEXT_MODEL_DEFAULTS = Object.freeze({
258
259
  "gpt-6-astra": 272e3
259
260
  });
260
261
  const normalizeAutoQuotaRetry = (value) => typeof value === "boolean" ? value : false;
262
+ const normalizeDisabledModels = (value) => Array.isArray(value) ? [...new Set(value.filter((id) => typeof id === "string" && /^[a-z][a-z0-9._-]{0,79}$/u.test(id)))].slice(0, 100) : [];
261
263
  const normalizeOutputVerbosity = (value) => [
262
264
  "default",
263
265
  "low",
@@ -1277,9 +1279,29 @@ function subagentThreadPolicy(parent, policy, requested = {}) {
1277
1279
  modelProvider: "openai",
1278
1280
  approvalPolicy: "never",
1279
1281
  sandbox: policy.mode,
1280
- config: effort === void 0 ? {} : { model_reasoning_effort: effort }
1282
+ config: {
1283
+ ...CHILD_FEATURES_OFF,
1284
+ ...effort === void 0 ? {} : { model_reasoning_effort: effort }
1285
+ }
1281
1286
  };
1282
1287
  }
1288
+ /**
1289
+ * A ChatGPT-authenticated Codex child would otherwise expose the account's
1290
+ * connected apps (calendar, site deploys, ...) and plugin installs, all
1291
+ * auto-approved. A DSH subtask gets the workspace tools only.
1292
+ */
1293
+ const CHILD_FEATURES_OFF = Object.freeze({ features: Object.freeze(Object.fromEntries([
1294
+ "apps",
1295
+ "plugins",
1296
+ "plugin_sharing",
1297
+ "remote_plugin",
1298
+ "tool_suggest",
1299
+ "browser_use",
1300
+ "browser_use_external",
1301
+ "browser_use_full_cdp_access",
1302
+ "computer_use",
1303
+ "in_app_browser"
1304
+ ].map((name) => [name, false]))) });
1283
1305
  /** Reuse the official DSH process/turn provider; keep only subscription auth here. */
1284
1306
  function createSubscriptionSubagent({ ctx, nativeHome, resolveAuth, store, refresh, loadRuntime, maintenance = () => false }) {
1285
1307
  let runtime;
@@ -1385,6 +1407,20 @@ function createSubscriptionSubagent({ ctx, nativeHome, resolveAuth, store, refre
1385
1407
  }
1386
1408
  };
1387
1409
  }
1410
+ /**
1411
+ * DSH 0.2 mounts Agent preset rows once, at boot and before plugins load, so the
1412
+ * Loader never lists them and `internal/config` never reaches a later listener.
1413
+ * Read them from the preset registry; a host without it simply has none.
1414
+ */
1415
+ function presetEntries(ctx) {
1416
+ try {
1417
+ const generations = ctx.get?.("agentPresets")?.generations;
1418
+ if (!(generations instanceof Map)) return [];
1419
+ return [...generations.values()].flatMap((generation) => [...generation.mount?.tree?.entries?.() ?? []]);
1420
+ } catch {
1421
+ return [];
1422
+ }
1423
+ }
1388
1424
  /** Change only standard independent spawn tools; leave fork/custom tools untouched. */
1389
1425
  function createSubagentBackendSwitcher({ entries, prepare, persist }) {
1390
1426
  const originals = /* @__PURE__ */ new Map();
@@ -3010,7 +3046,7 @@ async function readSubscriptionCredentials(getAuth, readCredential, signal) {
3010
3046
  }
3011
3047
  //#endregion
3012
3048
  //#region src/version.js
3013
- const PACKAGE_VERSION = "2.2.6";
3049
+ const PACKAGE_VERSION = "2.2.8";
3014
3050
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
3015
3051
  //#endregion
3016
3052
  //#region src/model-catalog.js
@@ -5787,6 +5823,7 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
5787
5823
  return {
5788
5824
  ok: true,
5789
5825
  value: {
5826
+ availableModels: Array.isArray(value?.availableModels) ? value.availableModels : [],
5790
5827
  contextModels: Array.isArray(value?.contextModels) ? value.contextModels : [],
5791
5828
  verbosityModels: Array.isArray(value?.verbosityModels) ? value.verbosityModels : [],
5792
5829
  fastModels: Array.isArray(value?.fastModels) ? value.fastModels : [],
@@ -5801,6 +5838,11 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
5801
5838
  signal.throwIfAborted();
5802
5839
  if (endpoint === "preferences/update") {
5803
5840
  const patch = capabilityPatch(payload);
5841
+ if (Object.hasOwn(payload ?? {}, "disabledModels")) {
5842
+ const disabled = payload[DISABLED_MODELS_FIELD];
5843
+ if (!Array.isArray(disabled) || disabled.length > 100 || disabled.length !== normalizeDisabledModels(disabled).length) return publicError("internal", "Invalid disabled models preference");
5844
+ patch[DISABLED_MODELS_FIELD] = disabled;
5845
+ }
5804
5846
  if (Object.hasOwn(payload ?? {}, "autoQuotaRetry")) {
5805
5847
  if (typeof payload["autoQuotaRetry"] !== "boolean") return publicError("internal", "Invalid automatic quota retry preference");
5806
5848
  patch[AUTO_QUOTA_RETRY_FIELD] = payload[AUTO_QUOTA_RETRY_FIELD];
@@ -5942,6 +5984,7 @@ function createSearchProviderSwitcher(loader) {
5942
5984
  });
5943
5985
  }
5944
5986
  const settingsFields = {
5987
+ [DISABLED_MODELS_FIELD]: z.transform(z.array(z.string()).max(100), normalizeDisabledModels).default([]),
5945
5988
  [AUTO_QUOTA_RETRY_FIELD]: z.boolean().default(false),
5946
5989
  ...Object.fromEntries(Object.entries(PREFERENCE_FIELDS).map(([field, rule]) => [field, rule.default === void 0 ? z.union(rule.choices) : z.union(rule.choices).default(rule.default)])),
5947
5990
  imageModel: z.union(Object.keys(IMAGE_MODELS)).default(DEFAULT_IMAGE_MODEL),
@@ -6028,29 +6071,37 @@ function apply(ctx, config = {}) {
6028
6071
  runNetwork: network.run
6029
6072
  });
6030
6073
  const preferences = {
6031
- status: () => ({
6032
- [AUTO_QUOTA_RETRY_FIELD]: normalizeAutoQuotaRetry(settings.get()[AUTO_QUOTA_RETRY_FIELD]),
6033
- compactionMode: settings.get().compactionMode ?? "dsh",
6034
- connectionMode: settings.get().connectionMode ?? "sse",
6035
- subagentBackend: settings.get().subagentBackend ?? "dsh",
6036
- subagentBackendAvailable: subagentBackend !== void 0,
6037
- subagentRuntimeInstalled: inspectSubagentRuntime().installed,
6038
- ...readCapabilitySettings(settings.get()),
6039
- [QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(settings.get()[QUICK_QUOTA_MODE_FIELD], settings.get()[LEGACY_QUICK_QUOTA_FIELD]),
6040
- [SEARCH_PROVIDER_FIELD]: settings.get()[SEARCH_PROVIDER_FIELD],
6041
- [SPEED_MODE_FIELD]: settings.get()[SPEED_MODE_FIELD],
6042
- [OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(settings.get()[OUTPUT_VERBOSITY_FIELD]),
6043
- [INPUT_IMAGE_DETAIL_FIELD]: normalizeInputImageDetail(settings.get()[INPUT_IMAGE_DETAIL_FIELD]),
6044
- [STREAM_IDLE_TIMEOUT_MINUTES_FIELD]: normalizeStreamIdleTimeoutMinutes(settings.get()[STREAM_IDLE_TIMEOUT_MINUTES_FIELD]),
6045
- [CONTEXT_MODE_FIELD]: normalizeContextMode(settings.get()[CONTEXT_MODE_FIELD]),
6046
- [CUSTOM_CONTEXT_WINDOW_FIELD]: normalizeCustomContextWindow(settings.get()[CUSTOM_CONTEXT_WINDOW_FIELD]),
6047
- ...Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [field, normalizeCustomContextWindow(settings.get()[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])])),
6048
- contextModels: contextModelGroups(modelCatalog.getModels(baseProvider.getModels())),
6049
- catalogStatus: modelCatalog.status(),
6050
- verbosityModels: provider.getModels().filter((model) => modelCatalog.metadata(model.id)?.supportVerbosity ?? model.id !== "gpt-5.3-codex-spark").map((model) => model.id),
6051
- fastModels: provider.getModels().filter((model) => modelCatalog.metadata(model.id)?.supportsFast ?? supportsCodexFastMode(model.id)).map((model) => model.id),
6052
- writable: ctx.settings.writable
6053
- }),
6074
+ status: () => {
6075
+ const models = modelCatalog.getModels(baseProvider.getModels());
6076
+ return {
6077
+ [DISABLED_MODELS_FIELD]: normalizeDisabledModels(settings.get()[DISABLED_MODELS_FIELD]),
6078
+ [AUTO_QUOTA_RETRY_FIELD]: normalizeAutoQuotaRetry(settings.get()[AUTO_QUOTA_RETRY_FIELD]),
6079
+ compactionMode: settings.get().compactionMode ?? "dsh",
6080
+ connectionMode: settings.get().connectionMode ?? "sse",
6081
+ subagentBackend: settings.get().subagentBackend ?? "dsh",
6082
+ subagentBackendAvailable: subagentBackend !== void 0,
6083
+ subagentRuntimeInstalled: inspectSubagentRuntime().installed,
6084
+ ...readCapabilitySettings(settings.get()),
6085
+ [QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(settings.get()[QUICK_QUOTA_MODE_FIELD], settings.get()[LEGACY_QUICK_QUOTA_FIELD]),
6086
+ [SEARCH_PROVIDER_FIELD]: settings.get()[SEARCH_PROVIDER_FIELD],
6087
+ [SPEED_MODE_FIELD]: settings.get()[SPEED_MODE_FIELD],
6088
+ [OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(settings.get()[OUTPUT_VERBOSITY_FIELD]),
6089
+ [INPUT_IMAGE_DETAIL_FIELD]: normalizeInputImageDetail(settings.get()[INPUT_IMAGE_DETAIL_FIELD]),
6090
+ [STREAM_IDLE_TIMEOUT_MINUTES_FIELD]: normalizeStreamIdleTimeoutMinutes(settings.get()[STREAM_IDLE_TIMEOUT_MINUTES_FIELD]),
6091
+ [CONTEXT_MODE_FIELD]: normalizeContextMode(settings.get()[CONTEXT_MODE_FIELD]),
6092
+ [CUSTOM_CONTEXT_WINDOW_FIELD]: normalizeCustomContextWindow(settings.get()[CUSTOM_CONTEXT_WINDOW_FIELD]),
6093
+ ...Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [field, normalizeCustomContextWindow(settings.get()[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])])),
6094
+ contextModels: contextModelGroups(models),
6095
+ availableModels: models.map(({ id, name }) => ({
6096
+ id,
6097
+ name
6098
+ })),
6099
+ catalogStatus: modelCatalog.status(),
6100
+ verbosityModels: provider.getModels().filter((model) => modelCatalog.metadata(model.id)?.supportVerbosity ?? model.id !== "gpt-5.3-codex-spark").map((model) => model.id),
6101
+ fastModels: provider.getModels().filter((model) => modelCatalog.metadata(model.id)?.supportsFast ?? supportsCodexFastMode(model.id)).map((model) => model.id),
6102
+ writable: ctx.settings.writable
6103
+ };
6104
+ },
6054
6105
  update: async (patch) => {
6055
6106
  if (Object.hasOwn(patch, "subagentBackend")) {
6056
6107
  if (!subagentBackend) throw new Error("DSH subagent services are unavailable");
@@ -6113,7 +6164,7 @@ function apply(ctx, config = {}) {
6113
6164
  });
6114
6165
  scoped.subagents.registerProvider(instance.provider);
6115
6166
  const switcher = createSubagentBackendSwitcher({
6116
- entries: () => scoped.loader.entries(),
6167
+ entries: () => [...scoped.loader.entries(), ...presetEntries(scoped)],
6117
6168
  prepare: instance.prepare,
6118
6169
  persist: (mode) => settings.update({ subagentBackend: mode })
6119
6170
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
4
4
  "description": "Use ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",