codex-to-im 1.0.57 → 1.0.58

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
  }
@@ -12942,6 +12956,28 @@ function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
12942
12956
  return null;
12943
12957
  }
12944
12958
  }
12959
+ function parseReasoningLevel(value) {
12960
+ if (typeof value !== "string") return void 0;
12961
+ const trimmed = value.trim();
12962
+ return trimmed ? trimmed : void 0;
12963
+ }
12964
+ function parseSupportedReasoningLevels(value) {
12965
+ if (!Array.isArray(value)) return [];
12966
+ const seen = /* @__PURE__ */ new Set();
12967
+ const levels = [];
12968
+ for (const level of value) {
12969
+ if (!level || typeof level !== "object") continue;
12970
+ const raw = level;
12971
+ const effort = parseReasoningLevel(raw.effort);
12972
+ if (!effort || seen.has(effort)) continue;
12973
+ seen.add(effort);
12974
+ levels.push({
12975
+ effort,
12976
+ description: typeof raw.description === "string" ? raw.description.trim() : ""
12977
+ });
12978
+ }
12979
+ return levels;
12980
+ }
12945
12981
  function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
12946
12982
  try {
12947
12983
  const raw = fs6.readFileSync(cachePath, "utf-8");
@@ -12954,11 +12990,14 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
12954
12990
  const slug = model.slug.trim();
12955
12991
  if (seen.has(slug)) continue;
12956
12992
  seen.add(slug);
12993
+ const defaultReasoningLevel = parseReasoningLevel(model.default_reasoning_level);
12957
12994
  models.push({
12958
12995
  slug,
12959
12996
  displayName: typeof model.display_name === "string" && model.display_name.trim() ? model.display_name.trim() : slug,
12960
12997
  visibility: typeof model.visibility === "string" && model.visibility.trim() ? model.visibility.trim() : "list",
12961
- supportedInApi: model.supported_in_api === true
12998
+ supportedInApi: model.supported_in_api === true,
12999
+ supportedReasoningLevels: parseSupportedReasoningLevels(model.supported_reasoning_levels),
13000
+ ...defaultReasoningLevel ? { defaultReasoningLevel } : {}
12962
13001
  });
12963
13002
  }
12964
13003
  return models;
@@ -13276,7 +13315,7 @@ ${attachmentResult.error || "\u672A\u77E5\u9519\u8BEF"}`,
13276
13315
 
13277
13316
  // src/lib/bridge/command-dispatch.ts
13278
13317
  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`";
13318
+ 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
13319
  function parseForceFlag(args) {
13281
13320
  const forcePattern = /(^|\s)--force(?=\s|$)/;
13282
13321
  const force = forcePattern.test(args);
@@ -13596,7 +13635,7 @@ async function handleBridgeCommand(adapter, msg, text2, deps) {
13596
13635
  response = buildCommandFields(
13597
13636
  "\u5F53\u524D\u601D\u8003\u7EA7\u522B",
13598
13637
  [["\u7EA7\u522B", formatReasoningEffort(resolveEffectiveReasoningEffort(session))]],
13599
- [REASONING_OPTIONS_TEXT, "\u53D1\u9001 `/r 4` \u6216 `/r high` \u53EF\u5207\u6362\u3002"],
13638
+ [REASONING_OPTIONS_TEXT, "\u53D1\u9001 `/r high` \u53EF\u5207\u6362\u3002"],
13600
13639
  responseParseMode === "Markdown"
13601
13640
  );
13602
13641
  break;
@@ -13605,8 +13644,8 @@ async function handleBridgeCommand(adapter, msg, text2, deps) {
13605
13644
  if (!reasoning) {
13606
13645
  response = buildCommandFields(
13607
13646
  "\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],
13647
+ [["\u547D\u4EE4", "`/reasoning low|medium|high|xhigh|max|ultra`"]],
13648
+ ["\u4E5F\u652F\u6301\u517C\u5BB9\u6570\u5B57\uFF1A`/reasoning 1|2|3|4|5|6|7`", REASONING_OPTIONS_TEXT],
13610
13649
  responseParseMode === "Markdown"
13611
13650
  );
13612
13651
  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";
@@ -7554,6 +7560,28 @@ function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
7554
7560
  return null;
7555
7561
  }
7556
7562
  }
7563
+ function parseReasoningLevel(value) {
7564
+ if (typeof value !== "string") return void 0;
7565
+ const trimmed = value.trim();
7566
+ return trimmed ? trimmed : void 0;
7567
+ }
7568
+ function parseSupportedReasoningLevels(value) {
7569
+ if (!Array.isArray(value)) return [];
7570
+ const seen = /* @__PURE__ */ new Set();
7571
+ const levels = [];
7572
+ for (const level of value) {
7573
+ if (!level || typeof level !== "object") continue;
7574
+ const raw = level;
7575
+ const effort = parseReasoningLevel(raw.effort);
7576
+ if (!effort || seen.has(effort)) continue;
7577
+ seen.add(effort);
7578
+ levels.push({
7579
+ effort,
7580
+ description: typeof raw.description === "string" ? raw.description.trim() : ""
7581
+ });
7582
+ }
7583
+ return levels;
7584
+ }
7557
7585
  function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
7558
7586
  try {
7559
7587
  const raw = fs8.readFileSync(cachePath, "utf-8");
@@ -7566,11 +7594,14 @@ function listCachedCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH) {
7566
7594
  const slug = model.slug.trim();
7567
7595
  if (seen.has(slug)) continue;
7568
7596
  seen.add(slug);
7597
+ const defaultReasoningLevel = parseReasoningLevel(model.default_reasoning_level);
7569
7598
  models.push({
7570
7599
  slug,
7571
7600
  displayName: typeof model.display_name === "string" && model.display_name.trim() ? model.display_name.trim() : slug,
7572
7601
  visibility: typeof model.visibility === "string" && model.visibility.trim() ? model.visibility.trim() : "list",
7573
- supportedInApi: model.supported_in_api === true
7602
+ supportedInApi: model.supported_in_api === true,
7603
+ supportedReasoningLevels: parseSupportedReasoningLevels(model.supported_reasoning_levels),
7604
+ ...defaultReasoningLevel ? { defaultReasoningLevel } : {}
7574
7605
  });
7575
7606
  }
7576
7607
  return models;
@@ -7586,8 +7617,7 @@ function listSelectableCodexModels(cachePath = DEFAULT_CODEX_MODELS_CACHE_PATH)
7586
7617
  var port = 4781;
7587
7618
  var serverStartTime = (/* @__PURE__ */ new Date()).toISOString();
7588
7619
  var AUTH_COOKIE_NAME = "cti_ui_auth";
7589
- var availableCodexModels = listSelectableCodexModels();
7590
- var availableCodexModelSlugs = new Set(availableCodexModels.map((model) => model.slug));
7620
+ var FALLBACK_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
7591
7621
  var FEISHU_CHAT_LABEL_TTL_MS = 5 * 60 * 1e3;
7592
7622
  var feishuChatLabelCache = /* @__PURE__ */ new Map();
7593
7623
  var feishuTenantTokenCache = /* @__PURE__ */ new Map();
@@ -7596,6 +7626,34 @@ function parsePreferredPort() {
7596
7626
  if (!Number.isInteger(raw) || raw <= 0 || raw > 65535) return 4781;
7597
7627
  return raw;
7598
7628
  }
7629
+ function getAvailableCodexModels() {
7630
+ return listSelectableCodexModels();
7631
+ }
7632
+ function getAvailableCodexModelSlugs(models = getAvailableCodexModels()) {
7633
+ return new Set(models.map((model) => model.slug));
7634
+ }
7635
+ function getCodexModelMetadata(modelSlug, models = getAvailableCodexModels()) {
7636
+ if (!modelSlug) return null;
7637
+ return models.find((model) => model.slug === modelSlug) || null;
7638
+ }
7639
+ function getSupportedReasoningEfforts(modelSlug, models = getAvailableCodexModels()) {
7640
+ const metadata = getCodexModelMetadata(modelSlug, models);
7641
+ const levels = metadata?.supportedReasoningLevels.map((level) => parseReasoningEffort(level.effort)).filter((level) => Boolean(level)) || [];
7642
+ return levels.length > 0 ? levels : FALLBACK_REASONING_EFFORTS;
7643
+ }
7644
+ function getDefaultReasoningEffort(modelSlug, models = getAvailableCodexModels()) {
7645
+ const metadata = getCodexModelMetadata(modelSlug, models);
7646
+ const supported = getSupportedReasoningEfforts(modelSlug, models);
7647
+ const catalogDefault = parseReasoningEffort(metadata?.defaultReasoningLevel);
7648
+ if (catalogDefault && supported.includes(catalogDefault)) return catalogDefault;
7649
+ if (supported.includes("medium")) return "medium";
7650
+ return supported[0] || "medium";
7651
+ }
7652
+ function isReasoningEffortSupportedByModel(value, modelSlug, models = getAvailableCodexModels()) {
7653
+ const metadata = getCodexModelMetadata(modelSlug, models);
7654
+ if (!metadata || metadata.supportedReasoningLevels.length === 0) return true;
7655
+ return getSupportedReasoningEfforts(modelSlug, models).includes(value);
7656
+ }
7599
7657
  async function canListen(portToCheck) {
7600
7658
  return await new Promise((resolve) => {
7601
7659
  const probe = net.createServer();
@@ -7796,12 +7854,16 @@ function isRemoteAuthenticated(request, config) {
7796
7854
  return timingSafeMatch(parseCookies(request).get(AUTH_COOKIE_NAME), config.uiAccessToken);
7797
7855
  }
7798
7856
  function configToPayload(config) {
7857
+ const availableModels = getAvailableCodexModels();
7858
+ const codexDefaultModel = readConfiguredCodexModel() || "";
7859
+ const effectiveModel = config.defaultModel || codexDefaultModel;
7799
7860
  return {
7800
7861
  runtime: config.runtime,
7801
7862
  defaultWorkspaceRoot: config.defaultWorkspaceRoot || "",
7802
7863
  defaultModel: config.defaultModel || "",
7803
- codexDefaultModel: readConfiguredCodexModel() || "",
7804
- availableModels: availableCodexModels,
7864
+ codexDefaultModel,
7865
+ availableModels,
7866
+ availableReasoningEfforts: getSupportedReasoningEfforts(effectiveModel, availableModels),
7805
7867
  defaultMode: config.defaultMode,
7806
7868
  historyMessageLimit: config.historyMessageLimit ?? 8,
7807
7869
  streamStatusIdleStartSeconds: config.streamStatusIdleStartSeconds ?? 180,
@@ -7816,7 +7878,17 @@ function configToPayload(config) {
7816
7878
  }
7817
7879
  function mergeConfig(payload) {
7818
7880
  const current = loadConfig();
7881
+ const availableCodexModels = getAvailableCodexModels();
7882
+ const availableCodexModelSlugs = getAvailableCodexModelSlugs(availableCodexModels);
7883
+ const codexDefaultModel = readConfiguredCodexModel() || "";
7819
7884
  const rawDefaultModel = typeof payload.defaultModel === "string" ? payload.defaultModel.trim() : void 0;
7885
+ const nextDefaultModel = rawDefaultModel === void 0 ? current.defaultModel : rawDefaultModel === "" ? void 0 : availableCodexModelSlugs.has(rawDefaultModel) ? rawDefaultModel : current.defaultModel;
7886
+ const currentEffectiveModel = current.defaultModel || codexDefaultModel;
7887
+ const nextEffectiveModel = nextDefaultModel || codexDefaultModel;
7888
+ const rawReasoningEffort = asString(payload.codexReasoningEffort);
7889
+ const parsedReasoningEffort = parseReasoningEffort(rawReasoningEffort);
7890
+ const preserveCurrentReasoning = parsedReasoningEffort && parsedReasoningEffort === current.codexReasoningEffort && nextEffectiveModel === currentEffectiveModel;
7891
+ const nextReasoningEffort = parsedReasoningEffort && (isReasoningEffortSupportedByModel(parsedReasoningEffort, nextEffectiveModel, availableCodexModels) || preserveCurrentReasoning) ? parsedReasoningEffort : getDefaultReasoningEffort(nextEffectiveModel, availableCodexModels);
7820
7892
  const uiAllowLan = payload.uiAllowLan === true;
7821
7893
  const requestedUiAccessToken = asString(payload.uiAccessToken);
7822
7894
  const uiAccessToken = requestedUiAccessToken || current.uiAccessToken || (uiAllowLan ? generateAccessToken() : void 0);
@@ -7825,14 +7897,14 @@ function mergeConfig(payload) {
7825
7897
  runtime: "codex",
7826
7898
  enabledChannels: current.enabledChannels,
7827
7899
  defaultWorkspaceRoot: asString(payload.defaultWorkspaceRoot),
7828
- defaultModel: rawDefaultModel === void 0 ? current.defaultModel : rawDefaultModel === "" ? void 0 : availableCodexModelSlugs.has(rawDefaultModel) ? rawDefaultModel : current.defaultModel,
7900
+ defaultModel: nextDefaultModel,
7829
7901
  defaultMode: payload.defaultMode === "plan" || payload.defaultMode === "ask" ? payload.defaultMode : "code",
7830
7902
  historyMessageLimit: asPositiveInt(payload.historyMessageLimit) || current.historyMessageLimit || 8,
7831
7903
  streamStatusIdleStartSeconds: asPositiveInt(payload.streamStatusIdleStartSeconds) || current.streamStatusIdleStartSeconds || 180,
7832
7904
  streamStatusCheckIntervalSeconds: asPositiveInt(payload.streamStatusCheckIntervalSeconds) || current.streamStatusCheckIntervalSeconds || 10,
7833
7905
  codexSkipGitRepoCheck: payload.codexSkipGitRepoCheck === true,
7834
7906
  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",
7907
+ codexReasoningEffort: nextReasoningEffort,
7836
7908
  uiAllowLan,
7837
7909
  uiAccessToken,
7838
7910
  channels: current.channels
@@ -9692,16 +9764,10 @@ function renderHtml() {
9692
9764
  </label>
9693
9765
  <label>
9694
9766
  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>
9767
+ <select id="codexReasoningEffort"></select>
9702
9768
  </label>
9703
9769
  </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>
9770
+ <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
9771
  <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
9772
  <div class="checkbox-row">
9707
9773
  <label class="checkbox"><input id="codexSkipGitRepoCheck" type="checkbox" checked /> \u5141\u8BB8\u5728\u672A\u4FE1\u4EFB Git \u76EE\u5F55\u8FD0\u884C Codex</label>
@@ -9776,7 +9842,7 @@ function renderHtml() {
9776
9842
  <div class="command-list">
9777
9843
  <div class="command-list-head"><div>\u547D\u4EE4</div><div>\u539F\u59CB\u547D\u4EE4</div><div>\u8BF4\u660E</div></div>
9778
9844
  <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>
9845
+ <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
9846
  <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
9847
  <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
9848
  <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 +9996,89 @@ function renderHtml() {
9930
9996
  select.value = currentValue;
9931
9997
  }
9932
9998
 
9999
+ function findAvailableModel(slug) {
10000
+ if (!slug) return null;
10001
+ return (state.availableModels || []).find((model) => model && model.slug === slug) || null;
10002
+ }
10003
+
10004
+ function fallbackReasoningLevels(config) {
10005
+ const efforts = Array.isArray(config && config.availableReasoningEfforts)
10006
+ ? config.availableReasoningEfforts
10007
+ : ['low', 'medium', 'high', 'xhigh'];
10008
+ return efforts
10009
+ .filter((effort) => typeof effort === 'string' && effort)
10010
+ .map((effort) => ({ effort, description: '' }));
10011
+ }
10012
+
10013
+ function reasoningLevelsForModel(config, modelSlug) {
10014
+ const model = findAvailableModel(modelSlug);
10015
+ const levels = model && Array.isArray(model.supportedReasoningLevels)
10016
+ ? model.supportedReasoningLevels
10017
+ : [];
10018
+ if (levels.length === 0) return fallbackReasoningLevels(config);
10019
+ return levels
10020
+ .filter((level) => level && typeof level.effort === 'string' && level.effort)
10021
+ .map((level) => ({
10022
+ effort: level.effort,
10023
+ description: typeof level.description === 'string' ? level.description : '',
10024
+ }));
10025
+ }
10026
+
10027
+ function defaultReasoningForModel(config, modelSlug, levels) {
10028
+ const model = findAvailableModel(modelSlug);
10029
+ const catalogDefault = model && typeof model.defaultReasoningLevel === 'string'
10030
+ ? model.defaultReasoningLevel
10031
+ : '';
10032
+ if (catalogDefault && levels.some((level) => level.effort === catalogDefault)) {
10033
+ return catalogDefault;
10034
+ }
10035
+ if (levels.some((level) => level.effort === 'medium')) return 'medium';
10036
+ return levels[0] ? levels[0].effort : (config.codexReasoningEffort || 'medium');
10037
+ }
10038
+
10039
+ function selectedDefaultModel(config) {
10040
+ const select = document.getElementById('defaultModel');
10041
+ const selected = select ? select.value : '';
10042
+ return selected || (config && config.codexDefaultModel) || '';
10043
+ }
10044
+
10045
+ function renderReasoningEffortOptions(config, options) {
10046
+ const opts = options || {};
10047
+ const select = document.getElementById('codexReasoningEffort');
10048
+ const modelSlug = selectedDefaultModel(config || {});
10049
+ const levels = reasoningLevelsForModel(config || {}, modelSlug);
10050
+ const currentValue = typeof opts.currentValue === 'string'
10051
+ ? opts.currentValue
10052
+ : (config && typeof config.codexReasoningEffort === 'string' ? config.codexReasoningEffort : 'medium');
10053
+ const preserveUnsupported = opts.preserveUnsupported !== false;
10054
+ const modelDefault = defaultReasoningForModel(config || {}, modelSlug, levels);
10055
+ const items = [];
10056
+ const seen = new Set();
10057
+
10058
+ for (const level of levels) {
10059
+ if (seen.has(level.effort)) continue;
10060
+ seen.add(level.effort);
10061
+ const labelParts = [level.effort];
10062
+ if (level.effort === modelDefault) labelParts.push('\u6A21\u578B\u9ED8\u8BA4');
10063
+ if (level.description) labelParts.push(level.description);
10064
+ items.push(
10065
+ '<option value="' + escapeHtml(level.effort) + '">' + escapeHtml(labelParts.join(' \xB7 ')) + '</option>'
10066
+ );
10067
+ }
10068
+
10069
+ if (currentValue && !seen.has(currentValue) && preserveUnsupported) {
10070
+ items.push(
10071
+ '<option value="' + escapeHtml(currentValue) + '">\u5F53\u524D\u914D\u7F6E\u503C\uFF08\u5F53\u524D\u6A21\u578B\u672A\u5217\u51FA\uFF09\uFF1A' + escapeHtml(currentValue) + '</option>'
10072
+ );
10073
+ }
10074
+
10075
+ select.innerHTML = items.join('');
10076
+ select.value = seen.has(currentValue) || preserveUnsupported ? currentValue : modelDefault;
10077
+ if (!select.value && items.length > 0) {
10078
+ select.value = modelDefault;
10079
+ }
10080
+ }
10081
+
9933
10082
  function shortId(value) {
9934
10083
  if (!value) return '-';
9935
10084
  return value.length > 14 ? value.slice(0, 8) + '...' + value.slice(-4) : value;
@@ -10794,9 +10943,9 @@ function renderHtml() {
10794
10943
  document.getElementById('streamStatusCheckIntervalSeconds').value = String(config.streamStatusCheckIntervalSeconds || 10);
10795
10944
  document.getElementById('defaultWorkspaceRoot').value = config.defaultWorkspaceRoot || '';
10796
10945
  renderDefaultModelOptions(config);
10946
+ renderReasoningEffortOptions(config);
10797
10947
  document.getElementById('codexSkipGitRepoCheck').checked = config.codexSkipGitRepoCheck === true;
10798
10948
  document.getElementById('codexSandboxMode').value = config.codexSandboxMode || 'workspace-write';
10799
- document.getElementById('codexReasoningEffort').value = config.codexReasoningEffort || 'medium';
10800
10949
  document.getElementById('uiAllowLan').checked = config.uiAllowLan === true;
10801
10950
  document.getElementById('uiAccessToken').value = config.uiAccessToken || '';
10802
10951
  renderUiAccess();
@@ -11204,6 +11353,13 @@ function renderHtml() {
11204
11353
  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
11354
  });
11206
11355
 
11356
+ document.getElementById('defaultModel').addEventListener('change', () => {
11357
+ renderReasoningEffortOptions(state.config || {}, {
11358
+ currentValue: document.getElementById('codexReasoningEffort').value,
11359
+ preserveUnsupported: false,
11360
+ });
11361
+ });
11362
+
11207
11363
  document.getElementById('saveConfigBtn').addEventListener('click', async () => {
11208
11364
  try {
11209
11365
  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.58",
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",