codex-to-im 1.0.57 → 1.0.59

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.md CHANGED
@@ -183,7 +183,7 @@ codex-to-im uninstall
183
183
  - `/new`:在当前正式会话目录下新建线程。
184
184
  - `/new <路径或项目名>`:按指定目录新建线程。
185
185
  - `/mode <ask|code|plan>`:切换运行模式。
186
- - `/reasoning <1-5>`:切换思考级别。
186
+ - `/reasoning <low|medium|high|xhigh|max|ultra>`:切换思考级别;数字 `1-5` 仍兼容旧映射,`6=max`、`7=ultra` 仅适用于支持这些档位的模型。
187
187
  - `/model`:查看当前模型和可选模型。
188
188
  - `/model <模型名>`:切换当前 IM 会话模型。
189
189
  - `/history`:查看当前线程历史摘要。
@@ -196,7 +196,7 @@ codex-to-im uninstall
196
196
 
197
197
  - 默认工作空间:用于 `/new my-project` 这类相对路径。
198
198
  - Codex 文件系统权限:如 `workspace-write`、`danger-full-access`。
199
- - Codex 思考级别:`1-5`。
199
+ - Codex 思考级别:按当前模型的 Codex catalog 展示,例如 `low`、`medium`、`high`、`xhigh`,部分新模型支持 `max`、`ultra`。
200
200
  - 默认模型:从本机可用模型中选择。
201
201
  - 反馈使用 markdown:控制 bridge 发到通道里的文本反馈是否走 markdown。
202
202
  - 允许局域网访问 Web 控制台:便于手机或局域网设备访问。
package/README_EN.md CHANGED
@@ -182,7 +182,7 @@ You can also specify a directory explicitly:
182
182
  - `/new`: create a new thread under the current formal session directory.
183
183
  - `/new <path or project name>`: create a new thread under the specified directory.
184
184
  - `/mode <ask|code|plan>`: change the runtime mode.
185
- - `/reasoning <1-5>`: change the reasoning effort.
185
+ - `/reasoning <low|medium|high|xhigh|max|ultra>`: change the reasoning effort; numeric `1-5` aliases remain compatible, while `6=max` and `7=ultra` only apply to models that support them.
186
186
  - `/model`: inspect the current model and available models.
187
187
  - `/model <model name>`: change the model for the current IM session.
188
188
  - `/history`: inspect the current thread history summary.
@@ -195,7 +195,7 @@ Common settings in the workbench include:
195
195
 
196
196
  - Default workspace root: used for relative paths such as `/new my-project`.
197
197
  - Codex filesystem permission: for example `workspace-write` or `danger-full-access`.
198
- - Codex reasoning effort: `1-5`.
198
+ - Codex reasoning effort: shown from the selected model's Codex catalog, for example `low`, `medium`, `high`, `xhigh`; some newer models also support `max` and `ultra`.
199
199
  - Default model: chosen from the models available on the local machine.
200
200
  - Use Markdown for feedback: controls whether bridge text feedback is sent through markdown rendering.
201
201
  - Allow LAN access to the Web console: useful when opening the workbench from a phone or another device on the same LAN.
package/dist/cli.mjs CHANGED
@@ -17,6 +17,15 @@ import os from "node:os";
17
17
  import path from "node:path";
18
18
 
19
19
  // src/runtime-options.ts
20
+ var RUNTIME_REASONING_EFFORTS = [
21
+ "minimal",
22
+ "low",
23
+ "medium",
24
+ "high",
25
+ "xhigh",
26
+ "max",
27
+ "ultra"
28
+ ];
20
29
  function parseSandboxMode(value) {
21
30
  if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") {
22
31
  return value;
@@ -24,10 +33,7 @@ function parseSandboxMode(value) {
24
33
  return void 0;
25
34
  }
26
35
  function parseReasoningEffort(value) {
27
- if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
28
- return value;
29
- }
30
- return void 0;
36
+ return RUNTIME_REASONING_EFFORTS.includes(value) ? value : void 0;
31
37
  }
32
38
  function normalizeChannelId(value) {
33
39
  return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
package/dist/daemon.mjs CHANGED
@@ -43,10 +43,7 @@ function normalizeSandboxMode(value, fallback = "workspace-write") {
43
43
  return parseSandboxMode(value) || fallback;
44
44
  }
45
45
  function parseReasoningEffort(value) {
46
- if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
47
- return value;
48
- }
49
- return void 0;
46
+ return RUNTIME_REASONING_EFFORTS.includes(value) ? value : void 0;
50
47
  }
51
48
  function normalizeReasoningEffort(value, fallback = "medium") {
52
49
  return parseReasoningEffort(value) || fallback;
@@ -54,9 +51,19 @@ function normalizeReasoningEffort(value, fallback = "medium") {
54
51
  function normalizeChannelId(value) {
55
52
  return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
56
53
  }
54
+ var RUNTIME_REASONING_EFFORTS;
57
55
  var init_runtime_options = __esm({
58
56
  "src/runtime-options.ts"() {
59
57
  "use strict";
58
+ RUNTIME_REASONING_EFFORTS = [
59
+ "minimal",
60
+ "low",
61
+ "medium",
62
+ "high",
63
+ "xhigh",
64
+ "max",
65
+ "ultra"
66
+ ];
60
67
  }
61
68
  });
62
69
 
@@ -11708,7 +11715,7 @@ function validateMode(mode) {
11708
11715
  }
11709
11716
 
11710
11717
  // src/lib/bridge/command-aliases.ts
11711
- var REASONING_LEVELS = ["minimal", "low", "medium", "high", "xhigh"];
11718
+ init_runtime_options();
11712
11719
  var DEFAULT_DESKTOP_THREAD_LIST_LIMIT = 10;
11713
11720
  var MAX_DESKTOP_THREAD_LIST_LIMIT = 200;
11714
11721
  function parseListIndex(raw) {
@@ -11758,9 +11765,8 @@ function parseDesktopThreadListArgs(args) {
11758
11765
  function normalizeReasoningEffort2(raw) {
11759
11766
  const token = raw.trim().toLowerCase();
11760
11767
  if (!token) return null;
11761
- if (REASONING_LEVELS.includes(token)) {
11762
- return token;
11763
- }
11768
+ const named = parseReasoningEffort(token);
11769
+ if (named) return named;
11764
11770
  switch (token) {
11765
11771
  case "1":
11766
11772
  return "minimal";
@@ -11772,6 +11778,10 @@ function normalizeReasoningEffort2(raw) {
11772
11778
  return "high";
11773
11779
  case "5":
11774
11780
  return "xhigh";
11781
+ case "6":
11782
+ return "max";
11783
+ case "7":
11784
+ return "ultra";
11775
11785
  default:
11776
11786
  return null;
11777
11787
  }
@@ -11809,6 +11819,10 @@ function formatReasoningEffort(reasoning) {
11809
11819
  return "high (4)";
11810
11820
  case "xhigh":
11811
11821
  return "xhigh (5)";
11822
+ case "max":
11823
+ return "max (6)";
11824
+ case "ultra":
11825
+ return "ultra (7)";
11812
11826
  default:
11813
11827
  return reasoning;
11814
11828
  }
@@ -12920,6 +12934,52 @@ import os3 from "node:os";
12920
12934
  import path9 from "node:path";
12921
12935
  var DEFAULT_CODEX_CONFIG_PATH = path9.join(os3.homedir(), ".codex", "config.toml");
12922
12936
  var DEFAULT_CODEX_MODELS_CACHE_PATH = path9.join(os3.homedir(), ".codex", "models_cache.json");
12937
+ var REASONING_LEVEL_DESCRIPTIONS = {
12938
+ low: "Fast responses with lighter reasoning",
12939
+ medium: "Balances speed and reasoning depth for everyday tasks",
12940
+ high: "Greater reasoning depth for complex problems",
12941
+ xhigh: "Extra high reasoning depth for complex problems",
12942
+ max: "Maximum reasoning depth for very hard problems",
12943
+ ultra: "Ultra reasoning depth for the hardest problems"
12944
+ };
12945
+ function buildReasoningLevels(efforts) {
12946
+ return efforts.map((effort) => ({
12947
+ effort,
12948
+ description: REASONING_LEVEL_DESCRIPTIONS[effort] || ""
12949
+ }));
12950
+ }
12951
+ function cloneCodexModel(model) {
12952
+ return {
12953
+ ...model,
12954
+ supportedReasoningLevels: model.supportedReasoningLevels.map((level) => ({ ...level }))
12955
+ };
12956
+ }
12957
+ var KNOWN_CODEX_MODEL_FALLBACKS = [
12958
+ {
12959
+ slug: "gpt-5.6-sol",
12960
+ displayName: "GPT-5.6-Sol",
12961
+ visibility: "list",
12962
+ supportedInApi: true,
12963
+ defaultReasoningLevel: "low",
12964
+ supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max", "ultra"])
12965
+ },
12966
+ {
12967
+ slug: "gpt-5.6-terra",
12968
+ displayName: "GPT-5.6-Terra",
12969
+ visibility: "list",
12970
+ supportedInApi: true,
12971
+ defaultReasoningLevel: "medium",
12972
+ supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max", "ultra"])
12973
+ },
12974
+ {
12975
+ slug: "gpt-5.6-luna",
12976
+ displayName: "GPT-5.6-Luna",
12977
+ visibility: "list",
12978
+ supportedInApi: true,
12979
+ defaultReasoningLevel: "medium",
12980
+ supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max"])
12981
+ }
12982
+ ];
12923
12983
  function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
12924
12984
  try {
12925
12985
  const raw = fs6.readFileSync(configPath, "utf-8");
@@ -12942,6 +13002,28 @@ function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
12942
13002
  return null;
12943
13003
  }
12944
13004
  }
13005
+ function parseReasoningLevel(value) {
13006
+ if (typeof value !== "string") return void 0;
13007
+ const trimmed = value.trim();
13008
+ return trimmed ? trimmed : void 0;
13009
+ }
13010
+ function parseSupportedReasoningLevels(value) {
13011
+ if (!Array.isArray(value)) return [];
13012
+ const seen = /* @__PURE__ */ new Set();
13013
+ const levels = [];
13014
+ for (const level of value) {
13015
+ if (!level || typeof level !== "object") continue;
13016
+ const raw = level;
13017
+ const effort = parseReasoningLevel(raw.effort);
13018
+ if (!effort || seen.has(effort)) continue;
13019
+ seen.add(effort);
13020
+ levels.push({
13021
+ effort,
13022
+ description: typeof raw.description === "string" ? raw.description.trim() : ""
13023
+ });
13024
+ }
13025
+ return levels;
13026
+ }
12945
13027
  function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
12946
13028
  try {
12947
13029
  const raw = fs6.readFileSync(cachePath, "utf-8");
@@ -12954,11 +13036,14 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
12954
13036
  const slug = model.slug.trim();
12955
13037
  if (seen.has(slug)) continue;
12956
13038
  seen.add(slug);
13039
+ const defaultReasoningLevel = parseReasoningLevel(model.default_reasoning_level);
12957
13040
  models.push({
12958
13041
  slug,
12959
13042
  displayName: typeof model.display_name === "string" && model.display_name.trim() ? model.display_name.trim() : slug,
12960
13043
  visibility: typeof model.visibility === "string" && model.visibility.trim() ? model.visibility.trim() : "list",
12961
- supportedInApi: model.supported_in_api === true
13044
+ supportedInApi: model.supported_in_api === true,
13045
+ supportedReasoningLevels: parseSupportedReasoningLevels(model.supported_reasoning_levels),
13046
+ ...defaultReasoningLevel ? { defaultReasoningLevel } : {}
12962
13047
  });
12963
13048
  }
12964
13049
  return models;
@@ -12966,13 +13051,50 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
12966
13051
  return [];
12967
13052
  }
12968
13053
  }
12969
- function listSelectableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
12970
- return listCachedCodexModels(cachePath).filter((model) => model.visibility !== "hide");
13054
+ function buildSyntheticCodexModel(slug) {
13055
+ const normalized = slug.trim();
13056
+ if (!normalized) return null;
13057
+ return {
13058
+ slug: normalized,
13059
+ displayName: normalized,
13060
+ visibility: "list",
13061
+ supportedInApi: true,
13062
+ supportedReasoningLevels: []
13063
+ };
13064
+ }
13065
+ function listAvailableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH, additionalModelSlugs = []) {
13066
+ const cachedModels = listCachedCodexModels(cachePath);
13067
+ const hiddenCachedSlugs = new Set(
13068
+ cachedModels.filter((model) => model.visibility === "hide").map((model) => model.slug)
13069
+ );
13070
+ const merged = [];
13071
+ const seen = /* @__PURE__ */ new Set();
13072
+ const addModel = (model, force = false) => {
13073
+ if (!force && hiddenCachedSlugs.has(model.slug)) return;
13074
+ if (seen.has(model.slug)) return;
13075
+ seen.add(model.slug);
13076
+ merged.push(cloneCodexModel(model));
13077
+ };
13078
+ for (const model of cachedModels) {
13079
+ if (model.visibility !== "hide") {
13080
+ addModel(model, true);
13081
+ }
13082
+ }
13083
+ for (const model of KNOWN_CODEX_MODEL_FALLBACKS) {
13084
+ addModel(model);
13085
+ }
13086
+ for (const slug of additionalModelSlugs) {
13087
+ const syntheticModel = buildSyntheticCodexModel(slug);
13088
+ if (syntheticModel) {
13089
+ addModel(syntheticModel, true);
13090
+ }
13091
+ }
13092
+ return merged;
12971
13093
  }
12972
- function findSelectableCodexModel(slug, cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
13094
+ function findAvailableCodexModel(slug, cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH, additionalModelSlugs = []) {
12973
13095
  const normalized = slug.trim();
12974
13096
  if (!normalized) return null;
12975
- return listSelectableCodexModels(cachePath).find((model) => model.slug === normalized) || null;
13097
+ return listAvailableCodexModels(cachePath, additionalModelSlugs).find((model) => model.slug === normalized) || null;
12976
13098
  }
12977
13099
  function isCliOnlyCodexModel(model) {
12978
13100
  return !!model && model.supportedInApi === false;
@@ -12980,7 +13102,7 @@ function isCliOnlyCodexModel(model) {
12980
13102
 
12981
13103
  // src/lib/bridge/bridge-session-support.ts
12982
13104
  init_runtime_options();
12983
- var AVAILABLE_CODEX_MODELS = listSelectableCodexModels();
13105
+ var AVAILABLE_CODEX_MODELS = listAvailableCodexModels();
12984
13106
  var AVAILABLE_CODEX_MODEL_MAP = new Map(AVAILABLE_CODEX_MODELS.map((model) => [model.slug, model]));
12985
13107
  function getDisplayedDesktopThreads(limit) {
12986
13108
  try {
@@ -13029,7 +13151,7 @@ function getAvailableModelChoicesText() {
13029
13151
  return `\u53EF\u9009\u6A21\u578B\uFF1A${AVAILABLE_CODEX_MODELS.map((model) => formatDisplayedModel(model.slug)).join("\u3001")}`;
13030
13152
  }
13031
13153
  function getSelectableCodexModel(slug) {
13032
- return AVAILABLE_CODEX_MODEL_MAP.get(slug) || findSelectableCodexModel(slug);
13154
+ return AVAILABLE_CODEX_MODEL_MAP.get(slug) || findAvailableCodexModel(slug);
13033
13155
  }
13034
13156
  function resolveNewWorkingDirectory(rawArgs) {
13035
13157
  const trimmed = rawArgs.trim();
@@ -13276,7 +13398,7 @@ ${attachmentResult.error || "\u672A\u77E5\u9519\u8BEF"}`,
13276
13398
 
13277
13399
  // src/lib/bridge/command-dispatch.ts
13278
13400
  var MODE_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`code`\uFF08\u76F4\u63A5\u6267\u884C\uFF0C\u9ED8\u8BA4\uFF09 `plan`\uFF08\u5148\u5206\u6790\u518D\u884C\u52A8\uFF09 `ask`\uFF08\u8F7B\u5BF9\u8BDD / \u8349\u7A3F\uFF09";
13279
- var REASONING_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`1=minimal` `2=low` `3=medium` `4=high` `5=xhigh`";
13401
+ var REASONING_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`low` `medium` `high` `xhigh`\uFF0C\u90E8\u5206\u65B0\u6A21\u578B\u8FD8\u652F\u6301 `max`\u3001`ultra`\uFF1B\u6570\u5B57\u517C\u5BB9\uFF1A`1=minimal` `2=low` `3=medium` `4=high` `5=xhigh` `6=max` `7=ultra`";
13280
13402
  function parseForceFlag(args) {
13281
13403
  const forcePattern = /(^|\s)--force(?=\s|$)/;
13282
13404
  const force = forcePattern.test(args);
@@ -13596,7 +13718,7 @@ async function handleBridgeCommand(adapter, msg, text2, deps) {
13596
13718
  response = buildCommandFields(
13597
13719
  "\u5F53\u524D\u601D\u8003\u7EA7\u522B",
13598
13720
  [["\u7EA7\u522B", formatReasoningEffort(resolveEffectiveReasoningEffort(session))]],
13599
- [REASONING_OPTIONS_TEXT, "\u53D1\u9001 `/r 4` \u6216 `/r high` \u53EF\u5207\u6362\u3002"],
13721
+ [REASONING_OPTIONS_TEXT, "\u53D1\u9001 `/r high` \u53EF\u5207\u6362\u3002"],
13600
13722
  responseParseMode === "Markdown"
13601
13723
  );
13602
13724
  break;
@@ -13605,8 +13727,8 @@ async function handleBridgeCommand(adapter, msg, text2, deps) {
13605
13727
  if (!reasoning) {
13606
13728
  response = buildCommandFields(
13607
13729
  "\u601D\u8003\u7EA7\u522B\u7528\u6CD5",
13608
- [["\u547D\u4EE4", "`/reasoning minimal|low|medium|high|xhigh`"]],
13609
- ["\u4E5F\u652F\u6301\u5B8C\u6574\u547D\u4EE4\uFF1A`/reasoning 1|2|3|4|5`", REASONING_OPTIONS_TEXT],
13730
+ [["\u547D\u4EE4", "`/reasoning low|medium|high|xhigh|max|ultra`"]],
13731
+ ["\u4E5F\u652F\u6301\u517C\u5BB9\u6570\u5B57\uFF1A`/reasoning 1|2|3|4|5|6|7`", REASONING_OPTIONS_TEXT],
13610
13732
  responseParseMode === "Markdown"
13611
13733
  );
13612
13734
  break;
@@ -4576,6 +4576,15 @@ import os from "node:os";
4576
4576
  import path from "node:path";
4577
4577
 
4578
4578
  // src/runtime-options.ts
4579
+ var RUNTIME_REASONING_EFFORTS = [
4580
+ "minimal",
4581
+ "low",
4582
+ "medium",
4583
+ "high",
4584
+ "xhigh",
4585
+ "max",
4586
+ "ultra"
4587
+ ];
4579
4588
  function parseSandboxMode(value) {
4580
4589
  if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") {
4581
4590
  return value;
@@ -4583,10 +4592,7 @@ function parseSandboxMode(value) {
4583
4592
  return void 0;
4584
4593
  }
4585
4594
  function parseReasoningEffort(value) {
4586
- if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
4587
- return value;
4588
- }
4589
- return void 0;
4595
+ return RUNTIME_REASONING_EFFORTS.includes(value) ? value : void 0;
4590
4596
  }
4591
4597
  function normalizeChannelId(value) {
4592
4598
  return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
@@ -7532,6 +7538,52 @@ import os4 from "node:os";
7532
7538
  import path9 from "node:path";
7533
7539
  var DEFAULT_CODEX_CONFIG_PATH = path9.join(os4.homedir(), ".codex", "config.toml");
7534
7540
  var DEFAULT_CODEX_MODELS_CACHE_PATH = path9.join(os4.homedir(), ".codex", "models_cache.json");
7541
+ var REASONING_LEVEL_DESCRIPTIONS = {
7542
+ low: "Fast responses with lighter reasoning",
7543
+ medium: "Balances speed and reasoning depth for everyday tasks",
7544
+ high: "Greater reasoning depth for complex problems",
7545
+ xhigh: "Extra high reasoning depth for complex problems",
7546
+ max: "Maximum reasoning depth for very hard problems",
7547
+ ultra: "Ultra reasoning depth for the hardest problems"
7548
+ };
7549
+ function buildReasoningLevels(efforts) {
7550
+ return efforts.map((effort) => ({
7551
+ effort,
7552
+ description: REASONING_LEVEL_DESCRIPTIONS[effort] || ""
7553
+ }));
7554
+ }
7555
+ function cloneCodexModel(model) {
7556
+ return {
7557
+ ...model,
7558
+ supportedReasoningLevels: model.supportedReasoningLevels.map((level) => ({ ...level }))
7559
+ };
7560
+ }
7561
+ var KNOWN_CODEX_MODEL_FALLBACKS = [
7562
+ {
7563
+ slug: "gpt-5.6-sol",
7564
+ displayName: "GPT-5.6-Sol",
7565
+ visibility: "list",
7566
+ supportedInApi: true,
7567
+ defaultReasoningLevel: "low",
7568
+ supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max", "ultra"])
7569
+ },
7570
+ {
7571
+ slug: "gpt-5.6-terra",
7572
+ displayName: "GPT-5.6-Terra",
7573
+ visibility: "list",
7574
+ supportedInApi: true,
7575
+ defaultReasoningLevel: "medium",
7576
+ supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max", "ultra"])
7577
+ },
7578
+ {
7579
+ slug: "gpt-5.6-luna",
7580
+ displayName: "GPT-5.6-Luna",
7581
+ visibility: "list",
7582
+ supportedInApi: true,
7583
+ defaultReasoningLevel: "medium",
7584
+ supportedReasoningLevels: buildReasoningLevels(["low", "medium", "high", "xhigh", "max"])
7585
+ }
7586
+ ];
7535
7587
  function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
7536
7588
  try {
7537
7589
  const raw = fs8.readFileSync(configPath, "utf-8");
@@ -7554,6 +7606,28 @@ function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
7554
7606
  return null;
7555
7607
  }
7556
7608
  }
7609
+ function parseReasoningLevel(value) {
7610
+ if (typeof value !== "string") return void 0;
7611
+ const trimmed = value.trim();
7612
+ return trimmed ? trimmed : void 0;
7613
+ }
7614
+ function parseSupportedReasoningLevels(value) {
7615
+ if (!Array.isArray(value)) return [];
7616
+ const seen = /* @__PURE__ */ new Set();
7617
+ const levels = [];
7618
+ for (const level of value) {
7619
+ if (!level || typeof level !== "object") continue;
7620
+ const raw = level;
7621
+ const effort = parseReasoningLevel(raw.effort);
7622
+ if (!effort || seen.has(effort)) continue;
7623
+ seen.add(effort);
7624
+ levels.push({
7625
+ effort,
7626
+ description: typeof raw.description === "string" ? raw.description.trim() : ""
7627
+ });
7628
+ }
7629
+ return levels;
7630
+ }
7557
7631
  function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
7558
7632
  try {
7559
7633
  const raw = fs8.readFileSync(cachePath, "utf-8");
@@ -7566,11 +7640,14 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
7566
7640
  const slug = model.slug.trim();
7567
7641
  if (seen.has(slug)) continue;
7568
7642
  seen.add(slug);
7643
+ const defaultReasoningLevel = parseReasoningLevel(model.default_reasoning_level);
7569
7644
  models.push({
7570
7645
  slug,
7571
7646
  displayName: typeof model.display_name === "string" && model.display_name.trim() ? model.display_name.trim() : slug,
7572
7647
  visibility: typeof model.visibility === "string" && model.visibility.trim() ? model.visibility.trim() : "list",
7573
- supportedInApi: model.supported_in_api === true
7648
+ supportedInApi: model.supported_in_api === true,
7649
+ supportedReasoningLevels: parseSupportedReasoningLevels(model.supported_reasoning_levels),
7650
+ ...defaultReasoningLevel ? { defaultReasoningLevel } : {}
7574
7651
  });
7575
7652
  }
7576
7653
  return models;
@@ -7578,16 +7655,52 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
7578
7655
  return [];
7579
7656
  }
7580
7657
  }
7581
- function listSelectableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
7582
- return listCachedCodexModels(cachePath).filter((model) => model.visibility !== "hide");
7658
+ function buildSyntheticCodexModel(slug) {
7659
+ const normalized = slug.trim();
7660
+ if (!normalized) return null;
7661
+ return {
7662
+ slug: normalized,
7663
+ displayName: normalized,
7664
+ visibility: "list",
7665
+ supportedInApi: true,
7666
+ supportedReasoningLevels: []
7667
+ };
7668
+ }
7669
+ function listAvailableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH, additionalModelSlugs = []) {
7670
+ const cachedModels = listCachedCodexModels(cachePath);
7671
+ const hiddenCachedSlugs = new Set(
7672
+ cachedModels.filter((model) => model.visibility === "hide").map((model) => model.slug)
7673
+ );
7674
+ const merged = [];
7675
+ const seen = /* @__PURE__ */ new Set();
7676
+ const addModel = (model, force = false) => {
7677
+ if (!force && hiddenCachedSlugs.has(model.slug)) return;
7678
+ if (seen.has(model.slug)) return;
7679
+ seen.add(model.slug);
7680
+ merged.push(cloneCodexModel(model));
7681
+ };
7682
+ for (const model of cachedModels) {
7683
+ if (model.visibility !== "hide") {
7684
+ addModel(model, true);
7685
+ }
7686
+ }
7687
+ for (const model of KNOWN_CODEX_MODEL_FALLBACKS) {
7688
+ addModel(model);
7689
+ }
7690
+ for (const slug of additionalModelSlugs) {
7691
+ const syntheticModel = buildSyntheticCodexModel(slug);
7692
+ if (syntheticModel) {
7693
+ addModel(syntheticModel, true);
7694
+ }
7695
+ }
7696
+ return merged;
7583
7697
  }
7584
7698
 
7585
7699
  // src/ui-server.ts
7586
7700
  var port = 4781;
7587
7701
  var serverStartTime = (/* @__PURE__ */ new Date()).toISOString();
7588
7702
  var AUTH_COOKIE_NAME = "cti_ui_auth";
7589
- var availableCodexModels = listSelectableCodexModels();
7590
- var availableCodexModelSlugs = new Set(availableCodexModels.map((model) => model.slug));
7703
+ var FALLBACK_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
7591
7704
  var FEISHU_CHAT_LABEL_TTL_MS = 5 * 60 * 1e3;
7592
7705
  var feishuChatLabelCache = /* @__PURE__ */ new Map();
7593
7706
  var feishuTenantTokenCache = /* @__PURE__ */ new Map();
@@ -7596,6 +7709,34 @@ function parsePreferredPort() {
7596
7709
  if (!Number.isInteger(raw) || raw <= 0 || raw > 65535) return 4781;
7597
7710
  return raw;
7598
7711
  }
7712
+ function getAvailableCodexModels(additionalModelSlugs = []) {
7713
+ return listAvailableCodexModels(void 0, additionalModelSlugs);
7714
+ }
7715
+ function getAvailableCodexModelSlugs(models = getAvailableCodexModels()) {
7716
+ return new Set(models.map((model) => model.slug));
7717
+ }
7718
+ function getCodexModelMetadata(modelSlug, models = getAvailableCodexModels()) {
7719
+ if (!modelSlug) return null;
7720
+ return models.find((model) => model.slug === modelSlug) || null;
7721
+ }
7722
+ function getSupportedReasoningEfforts(modelSlug, models = getAvailableCodexModels()) {
7723
+ const metadata = getCodexModelMetadata(modelSlug, models);
7724
+ const levels = metadata?.supportedReasoningLevels.map((level) => parseReasoningEffort(level.effort)).filter((level) => Boolean(level)) || [];
7725
+ return levels.length > 0 ? levels : FALLBACK_REASONING_EFFORTS;
7726
+ }
7727
+ function getDefaultReasoningEffort(modelSlug, models = getAvailableCodexModels()) {
7728
+ const metadata = getCodexModelMetadata(modelSlug, models);
7729
+ const supported = getSupportedReasoningEfforts(modelSlug, models);
7730
+ const catalogDefault = parseReasoningEffort(metadata?.defaultReasoningLevel);
7731
+ if (catalogDefault && supported.includes(catalogDefault)) return catalogDefault;
7732
+ if (supported.includes("medium")) return "medium";
7733
+ return supported[0] || "medium";
7734
+ }
7735
+ function isReasoningEffortSupportedByModel(value, modelSlug, models = getAvailableCodexModels()) {
7736
+ const metadata = getCodexModelMetadata(modelSlug, models);
7737
+ if (!metadata || metadata.supportedReasoningLevels.length === 0) return true;
7738
+ return getSupportedReasoningEfforts(modelSlug, models).includes(value);
7739
+ }
7599
7740
  async function canListen(portToCheck) {
7600
7741
  return await new Promise((resolve) => {
7601
7742
  const probe = net.createServer();
@@ -7796,12 +7937,16 @@ function isRemoteAuthenticated(request, config) {
7796
7937
  return timingSafeMatch(parseCookies(request).get(AUTH_COOKIE_NAME), config.uiAccessToken);
7797
7938
  }
7798
7939
  function configToPayload(config) {
7940
+ const codexDefaultModel = readConfiguredCodexModel() || "";
7941
+ const availableModels = getAvailableCodexModels([config.defaultModel || "", codexDefaultModel]);
7942
+ const effectiveModel = config.defaultModel || codexDefaultModel;
7799
7943
  return {
7800
7944
  runtime: config.runtime,
7801
7945
  defaultWorkspaceRoot: config.defaultWorkspaceRoot || "",
7802
7946
  defaultModel: config.defaultModel || "",
7803
- codexDefaultModel: readConfiguredCodexModel() || "",
7804
- availableModels: availableCodexModels,
7947
+ codexDefaultModel,
7948
+ availableModels,
7949
+ availableReasoningEfforts: getSupportedReasoningEfforts(effectiveModel, availableModels),
7805
7950
  defaultMode: config.defaultMode,
7806
7951
  historyMessageLimit: config.historyMessageLimit ?? 8,
7807
7952
  streamStatusIdleStartSeconds: config.streamStatusIdleStartSeconds ?? 180,
@@ -7816,7 +7961,21 @@ function configToPayload(config) {
7816
7961
  }
7817
7962
  function mergeConfig(payload) {
7818
7963
  const current = loadConfig();
7964
+ const codexDefaultModel = readConfiguredCodexModel() || "";
7819
7965
  const rawDefaultModel = typeof payload.defaultModel === "string" ? payload.defaultModel.trim() : void 0;
7966
+ const availableCodexModels = getAvailableCodexModels([
7967
+ current.defaultModel || "",
7968
+ codexDefaultModel,
7969
+ rawDefaultModel || ""
7970
+ ]);
7971
+ const availableCodexModelSlugs = getAvailableCodexModelSlugs(availableCodexModels);
7972
+ const nextDefaultModel = rawDefaultModel === void 0 ? current.defaultModel : rawDefaultModel === "" ? void 0 : availableCodexModelSlugs.has(rawDefaultModel) ? rawDefaultModel : current.defaultModel;
7973
+ const currentEffectiveModel = current.defaultModel || codexDefaultModel;
7974
+ const nextEffectiveModel = nextDefaultModel || codexDefaultModel;
7975
+ const rawReasoningEffort = asString(payload.codexReasoningEffort);
7976
+ const parsedReasoningEffort = parseReasoningEffort(rawReasoningEffort);
7977
+ const preserveCurrentReasoning = parsedReasoningEffort && parsedReasoningEffort === current.codexReasoningEffort && nextEffectiveModel === currentEffectiveModel;
7978
+ const nextReasoningEffort = parsedReasoningEffort && (isReasoningEffortSupportedByModel(parsedReasoningEffort, nextEffectiveModel, availableCodexModels) || preserveCurrentReasoning) ? parsedReasoningEffort : getDefaultReasoningEffort(nextEffectiveModel, availableCodexModels);
7820
7979
  const uiAllowLan = payload.uiAllowLan === true;
7821
7980
  const requestedUiAccessToken = asString(payload.uiAccessToken);
7822
7981
  const uiAccessToken = requestedUiAccessToken || current.uiAccessToken || (uiAllowLan ? generateAccessToken() : void 0);
@@ -7825,14 +7984,14 @@ function mergeConfig(payload) {
7825
7984
  runtime: "codex",
7826
7985
  enabledChannels: current.enabledChannels,
7827
7986
  defaultWorkspaceRoot: asString(payload.defaultWorkspaceRoot),
7828
- defaultModel: rawDefaultModel === void 0 ? current.defaultModel : rawDefaultModel === "" ? void 0 : availableCodexModelSlugs.has(rawDefaultModel) ? rawDefaultModel : current.defaultModel,
7987
+ defaultModel: nextDefaultModel,
7829
7988
  defaultMode: payload.defaultMode === "plan" || payload.defaultMode === "ask" ? payload.defaultMode : "code",
7830
7989
  historyMessageLimit: asPositiveInt(payload.historyMessageLimit) || current.historyMessageLimit || 8,
7831
7990
  streamStatusIdleStartSeconds: asPositiveInt(payload.streamStatusIdleStartSeconds) || current.streamStatusIdleStartSeconds || 180,
7832
7991
  streamStatusCheckIntervalSeconds: asPositiveInt(payload.streamStatusCheckIntervalSeconds) || current.streamStatusCheckIntervalSeconds || 10,
7833
7992
  codexSkipGitRepoCheck: payload.codexSkipGitRepoCheck === true,
7834
7993
  codexSandboxMode: payload.codexSandboxMode === "read-only" || payload.codexSandboxMode === "danger-full-access" ? payload.codexSandboxMode : "workspace-write",
7835
- codexReasoningEffort: payload.codexReasoningEffort === "minimal" || payload.codexReasoningEffort === "low" || payload.codexReasoningEffort === "high" || payload.codexReasoningEffort === "xhigh" ? payload.codexReasoningEffort : "medium",
7994
+ codexReasoningEffort: nextReasoningEffort,
7836
7995
  uiAllowLan,
7837
7996
  uiAccessToken,
7838
7997
  channels: current.channels
@@ -9692,16 +9851,10 @@ function renderHtml() {
9692
9851
  </label>
9693
9852
  <label>
9694
9853
  Codex \u601D\u8003\u7EA7\u522B
9695
- <select id="codexReasoningEffort">
9696
- <option value="medium">medium</option>
9697
- <option value="minimal">minimal</option>
9698
- <option value="low">low</option>
9699
- <option value="high">high</option>
9700
- <option value="xhigh">xhigh</option>
9701
- </select>
9854
+ <select id="codexReasoningEffort"></select>
9702
9855
  </label>
9703
9856
  </div>
9704
- <div class="small">\u672A\u7ED1\u5B9A\u7684 IM \u804A\u5929\u4F1A\u5148\u8FDB\u5165\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B\uFF08\u7B49\u540C <code>/t 0</code>\uFF09\uFF1B\u201C\u9ED8\u8BA4\u5DE5\u4F5C\u7A7A\u95F4\u201D\u53EA\u7528\u4E8E <code>/new proj1</code> \u8FD9\u7C7B\u76F8\u5BF9\u9879\u76EE\u540D\u3002\u7559\u7A7A\u65F6\u4F1A\u6309\u5F53\u524D\u7CFB\u7EDF\u81EA\u52A8\u56DE\u9000\u5230 <code>~/cx2im</code>\u3002\u9ED8\u8BA4\u6A21\u578B\u5019\u9009\u9879\u6765\u81EA\u542F\u52A8\u65F6\u8BFB\u53D6\u7684 Codex \u6A21\u578B\u7F13\u5B58\uFF1A\u9690\u85CF\u6A21\u578B\u4E0D\u4F1A\u5C55\u793A\uFF0CCLI only \u6A21\u578B\u4F1A\u6807\u6210\u201C\u4EC5 IM / CLI\u201D\u3002\u7559\u7A7A\u5219\u7EE7\u7EED\u8DDF\u968F Codex \u5F53\u524D\u9ED8\u8BA4\u6A21\u578B\u3002\u6587\u4EF6\u7CFB\u7EDF\u6743\u9650\u662F\u5168\u5C40\u9ED8\u8BA4\u503C\uFF0C\u601D\u8003\u7EA7\u522B\u53EF\u5728 IM \u4F1A\u8BDD\u91CC\u518D\u5355\u72EC\u8986\u76D6\u3002\u4E0A\u6B21\u54CD\u5E94\u8DDD\u4ECA\u914D\u7F6E\u53EA\u5F71\u54CD\u98DE\u4E66\u957F\u4EFB\u52A1\u5E95\u90E8\u201C\u4E0A\u6B21\u54CD\u5E94\u8DDD\u4ECA X\u201D\u7684\u51FA\u73B0\u65F6\u673A\u3002</div>
9857
+ <div class="small">\u672A\u7ED1\u5B9A\u7684 IM \u804A\u5929\u4F1A\u5148\u8FDB\u5165\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B\uFF08\u7B49\u540C <code>/t 0</code>\uFF09\uFF1B\u201C\u9ED8\u8BA4\u5DE5\u4F5C\u7A7A\u95F4\u201D\u53EA\u7528\u4E8E <code>/new proj1</code> \u8FD9\u7C7B\u76F8\u5BF9\u9879\u76EE\u540D\u3002\u7559\u7A7A\u65F6\u4F1A\u6309\u5F53\u524D\u7CFB\u7EDF\u81EA\u52A8\u56DE\u9000\u5230 <code>~/cx2im</code>\u3002\u9ED8\u8BA4\u6A21\u578B\u5019\u9009\u9879\u6765\u81EA\u6309\u9700\u8BFB\u53D6\u7684 Codex \u6A21\u578B\u7F13\u5B58\uFF1A\u9690\u85CF\u6A21\u578B\u4E0D\u4F1A\u5C55\u793A\uFF0CCLI only \u6A21\u578B\u4F1A\u6807\u6210\u201C\u4EC5 IM / CLI\u201D\u3002\u7559\u7A7A\u5219\u7EE7\u7EED\u8DDF\u968F Codex \u5F53\u524D\u9ED8\u8BA4\u6A21\u578B\u3002\u6587\u4EF6\u7CFB\u7EDF\u6743\u9650\u662F\u5168\u5C40\u9ED8\u8BA4\u503C\uFF0C\u601D\u8003\u7EA7\u522B\u53EF\u5728 IM \u4F1A\u8BDD\u91CC\u518D\u5355\u72EC\u8986\u76D6\u3002\u4E0A\u6B21\u54CD\u5E94\u8DDD\u4ECA\u914D\u7F6E\u53EA\u5F71\u54CD\u98DE\u4E66\u957F\u4EFB\u52A1\u5E95\u90E8\u201C\u4E0A\u6B21\u54CD\u5E94\u8DDD\u4ECA X\u201D\u7684\u51FA\u73B0\u65F6\u673A\u3002</div>
9705
9858
  <div class="small">\u5F53\u524D\u9700\u8981\u91CD\u542F Bridge \u7684\u914D\u7F6E\uFF1A<code>Runtime</code>\u3001<code>\u5141\u8BB8\u5728\u672A\u4FE1\u4EFB Git \u76EE\u5F55\u8FD0\u884C Codex</code>\u3002\u901A\u9053\u5B9E\u4F8B\u7684\u63A5\u5165\u914D\u7F6E\u8BF7\u5728\u201C\u901A\u9053\u201D\u9875\u7EF4\u62A4\u3002</div>
9706
9859
  <div class="checkbox-row">
9707
9860
  <label class="checkbox"><input id="codexSkipGitRepoCheck" type="checkbox" checked /> \u5141\u8BB8\u5728\u672A\u4FE1\u4EFB Git \u76EE\u5F55\u8FD0\u884C Codex</label>
@@ -9776,7 +9929,7 @@ function renderHtml() {
9776
9929
  <div class="command-list">
9777
9930
  <div class="command-list-head"><div>\u547D\u4EE4</div><div>\u539F\u59CB\u547D\u4EE4</div><div>\u8BF4\u660E</div></div>
9778
9931
  <div class="command-item"><div class="command-col-command"><code>/m</code></div><div class="command-col-original"><code>/mode</code></div><div class="command-col-desc">\u67E5\u770B\u5F53\u524D\u6A21\u5F0F\uFF1B\u53EF\u9009 <code>code</code>\u3001<code>plan</code>\u3001<code>ask</code>\u3002</div></div>
9779
- <div class="command-item"><div class="command-col-command"><code>/r</code></div><div class="command-col-original"><code>/reasoning</code></div><div class="command-col-desc">\u67E5\u770B\u5F53\u524D\u601D\u8003\u7EA7\u522B\uFF1B\u53EF\u9009 <code>1=minimal</code>\u3001<code>2=low</code>\u3001<code>3=medium</code>\u3001<code>4=high</code>\u3001<code>5=xhigh</code>\u3002</div></div>
9932
+ <div class="command-item"><div class="command-col-command"><code>/r</code></div><div class="command-col-original"><code>/reasoning</code></div><div class="command-col-desc">\u67E5\u770B\u5F53\u524D\u601D\u8003\u7EA7\u522B\uFF1B\u53EF\u9009 <code>low</code>\u3001<code>medium</code>\u3001<code>high</code>\u3001<code>xhigh</code>\uFF0C\u90E8\u5206\u65B0\u6A21\u578B\u8FD8\u652F\u6301 <code>max</code>\u3001<code>ultra</code>\u3002</div></div>
9780
9933
  <div class="command-item"><div class="command-col-command"><code>/model [slug|default]</code></div><div class="command-col-original"><code>/model [slug|default]</code></div><div class="command-col-desc">\u67E5\u770B\u6216\u5207\u6362\u5F53\u524D IM \u4F1A\u8BDD\u4F7F\u7528\u7684\u6A21\u578B\uFF1BCLI only \u6A21\u578B\u4F1A\u6807\u6CE8\u201C\u4EC5 IM / CLI\u201D\uFF0C\u5171\u4EAB\u684C\u9762\u7EBF\u7A0B\u53EA\u5141\u8BB8\u67E5\u770B\u4E0D\u5141\u8BB8\u5207\u6362\u3002</div></div>
9781
9934
  <div class="command-item"><div class="command-col-command"><code>/t 0</code></div><div class="command-col-original"><code>/thread 0</code></div><div class="command-col-desc">\u5207\u6362\u5230\u5F53\u524D\u804A\u5929\u7684\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B\u3002</div></div>
9782
9935
  <div class="command-item"><div class="command-col-command"><code>/t 0 reset</code></div><div class="command-col-original"><code>/thread 0 reset</code></div><div class="command-col-desc">\u4E22\u5F03\u5F53\u524D\u8349\u7A3F\u4E0A\u4E0B\u6587\u5E76\u91CD\u5EFA\u4E00\u6761\u65B0\u7684\u8349\u7A3F\u7EBF\u7A0B\u3002</div></div>
@@ -9930,6 +10083,89 @@ function renderHtml() {
9930
10083
  select.value = currentValue;
9931
10084
  }
9932
10085
 
10086
+ function findAvailableModel(slug) {
10087
+ if (!slug) return null;
10088
+ return (state.availableModels || []).find((model) => model && model.slug === slug) || null;
10089
+ }
10090
+
10091
+ function fallbackReasoningLevels(config) {
10092
+ const efforts = Array.isArray(config && config.availableReasoningEfforts)
10093
+ ? config.availableReasoningEfforts
10094
+ : ['low', 'medium', 'high', 'xhigh'];
10095
+ return efforts
10096
+ .filter((effort) => typeof effort === 'string' && effort)
10097
+ .map((effort) => ({ effort, description: '' }));
10098
+ }
10099
+
10100
+ function reasoningLevelsForModel(config, modelSlug) {
10101
+ const model = findAvailableModel(modelSlug);
10102
+ const levels = model && Array.isArray(model.supportedReasoningLevels)
10103
+ ? model.supportedReasoningLevels
10104
+ : [];
10105
+ if (levels.length === 0) return fallbackReasoningLevels(config);
10106
+ return levels
10107
+ .filter((level) => level && typeof level.effort === 'string' && level.effort)
10108
+ .map((level) => ({
10109
+ effort: level.effort,
10110
+ description: typeof level.description === 'string' ? level.description : '',
10111
+ }));
10112
+ }
10113
+
10114
+ function defaultReasoningForModel(config, modelSlug, levels) {
10115
+ const model = findAvailableModel(modelSlug);
10116
+ const catalogDefault = model && typeof model.defaultReasoningLevel === 'string'
10117
+ ? model.defaultReasoningLevel
10118
+ : '';
10119
+ if (catalogDefault && levels.some((level) => level.effort === catalogDefault)) {
10120
+ return catalogDefault;
10121
+ }
10122
+ if (levels.some((level) => level.effort === 'medium')) return 'medium';
10123
+ return levels[0] ? levels[0].effort : (config.codexReasoningEffort || 'medium');
10124
+ }
10125
+
10126
+ function selectedDefaultModel(config) {
10127
+ const select = document.getElementById('defaultModel');
10128
+ const selected = select ? select.value : '';
10129
+ return selected || (config && config.codexDefaultModel) || '';
10130
+ }
10131
+
10132
+ function renderReasoningEffortOptions(config, options) {
10133
+ const opts = options || {};
10134
+ const select = document.getElementById('codexReasoningEffort');
10135
+ const modelSlug = selectedDefaultModel(config || {});
10136
+ const levels = reasoningLevelsForModel(config || {}, modelSlug);
10137
+ const currentValue = typeof opts.currentValue === 'string'
10138
+ ? opts.currentValue
10139
+ : (config && typeof config.codexReasoningEffort === 'string' ? config.codexReasoningEffort : 'medium');
10140
+ const preserveUnsupported = opts.preserveUnsupported !== false;
10141
+ const modelDefault = defaultReasoningForModel(config || {}, modelSlug, levels);
10142
+ const items = [];
10143
+ const seen = new Set();
10144
+
10145
+ for (const level of levels) {
10146
+ if (seen.has(level.effort)) continue;
10147
+ seen.add(level.effort);
10148
+ const labelParts = [level.effort];
10149
+ if (level.effort === modelDefault) labelParts.push('\u6A21\u578B\u9ED8\u8BA4');
10150
+ if (level.description) labelParts.push(level.description);
10151
+ items.push(
10152
+ '<option value="' + escapeHtml(level.effort) + '">' + escapeHtml(labelParts.join(' \xB7 ')) + '</option>'
10153
+ );
10154
+ }
10155
+
10156
+ if (currentValue && !seen.has(currentValue) && preserveUnsupported) {
10157
+ items.push(
10158
+ '<option value="' + escapeHtml(currentValue) + '">\u5F53\u524D\u914D\u7F6E\u503C\uFF08\u5F53\u524D\u6A21\u578B\u672A\u5217\u51FA\uFF09\uFF1A' + escapeHtml(currentValue) + '</option>'
10159
+ );
10160
+ }
10161
+
10162
+ select.innerHTML = items.join('');
10163
+ select.value = seen.has(currentValue) || preserveUnsupported ? currentValue : modelDefault;
10164
+ if (!select.value && items.length > 0) {
10165
+ select.value = modelDefault;
10166
+ }
10167
+ }
10168
+
9933
10169
  function shortId(value) {
9934
10170
  if (!value) return '-';
9935
10171
  return value.length > 14 ? value.slice(0, 8) + '...' + value.slice(-4) : value;
@@ -10794,9 +11030,9 @@ function renderHtml() {
10794
11030
  document.getElementById('streamStatusCheckIntervalSeconds').value = String(config.streamStatusCheckIntervalSeconds || 10);
10795
11031
  document.getElementById('defaultWorkspaceRoot').value = config.defaultWorkspaceRoot || '';
10796
11032
  renderDefaultModelOptions(config);
11033
+ renderReasoningEffortOptions(config);
10797
11034
  document.getElementById('codexSkipGitRepoCheck').checked = config.codexSkipGitRepoCheck === true;
10798
11035
  document.getElementById('codexSandboxMode').value = config.codexSandboxMode || 'workspace-write';
10799
- document.getElementById('codexReasoningEffort').value = config.codexReasoningEffort || 'medium';
10800
11036
  document.getElementById('uiAllowLan').checked = config.uiAllowLan === true;
10801
11037
  document.getElementById('uiAccessToken').value = config.uiAccessToken || '';
10802
11038
  renderUiAccess();
@@ -11204,6 +11440,13 @@ function renderHtml() {
11204
11440
  showMessage('configMessage', 'success', '\u5DF2\u751F\u6210\u65B0\u7684\u8BBF\u95EE token\uFF0C\u70B9\u51FB\u201C\u4FDD\u5B58\u914D\u7F6E\u201D\u540E\u751F\u6548\u3002');
11205
11441
  });
11206
11442
 
11443
+ document.getElementById('defaultModel').addEventListener('change', () => {
11444
+ renderReasoningEffortOptions(state.config || {}, {
11445
+ currentValue: document.getElementById('codexReasoningEffort').value,
11446
+ preserveUnsupported: false,
11447
+ });
11448
+ });
11449
+
11207
11450
  document.getElementById('saveConfigBtn').addEventListener('click', async () => {
11208
11451
  try {
11209
11452
  await saveConfig({ messageId: 'configMessage', scope: 'all' });
@@ -234,7 +234,7 @@ Web 工作台现在只展示自动启动状态;真正的启用和关闭请使
234
234
  - 另外,当前 `codex` runtime 下正文通常不是逐字流式输出;更常见的效果是先显示 `Thinking / Tool Progress`,正文在回答完成时一次性落到卡片里。
235
235
  - IM 里发送 `/history` 可以查看当前会话最近 N 条消息,N 由“基础配置”中的返回条数控制。
236
236
  - IM 里发送 `/history` 默认返回整理后的摘要;发送 `/history raw` 才会查看最近 N 条原始消息。
237
- - IM 里发送 `/reasoning high` 或 `/reasoning 4` 之类命令,可以只对当前会话覆盖思考级别。
237
+ - IM 里发送 `/reasoning high`、`/reasoning max` 或 `/reasoning 4` 之类命令,可以只对当前会话覆盖思考级别;具体可用档位取决于当前模型。
238
238
  - IM 里发送 `/thread 0` 会进入临时草稿线程,适合短讨论或临时想法。
239
239
  - “通道”页现在管理的是多个通道实例,而不是固定的一组飞书/微信配置。
240
240
  - 每个实例都可以有自己的别名;别名只用于区分不同聊天入口,不会改变 Codex 会话语义。
@@ -249,7 +249,7 @@ Web 工作台现在只展示自动启动状态;真正的启用和关闭请使
249
249
  - `/n` / `/new` 在当前正式会话目录下新建线程;这类线程当前只保证在 IM 中可继续,不会自动出现在 Codex Desktop 会话列表中
250
250
  - `/n proj1` / `/new proj1` 新建项目会话
251
251
  - `/m` / `/mode` 查看或切换模式,可选 `code` / `plan` / `ask`
252
- - `/r` / `/reasoning` 查看或切换思考级别,也支持 `1|2|3|4|5`
252
+ - `/r` / `/reasoning` 查看或切换思考级别,支持 `low|medium|high|xhigh|max|ultra`,数字 `1|2|3|4|5|6|7` 作为兼容别名
253
253
  - `/his` / `/history` 历史摘要,`/his raw` / `/history raw` 原始记录
254
254
  - `/t 0` / `/thread 0` 临时草稿线程
255
255
  - 如果 `测试 Codex` 失败,优先检查当前 Windows 用户下是否已经存在可用的 Codex 登录态或 API Key。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-to-im",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "description": "Installable Codex-to-IM bridge with local setup UI and background service",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/zhangle1987/codex-to-im#readme",