@chrysb/alphaclaw 0.9.29 → 0.9.31

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.
@@ -36,6 +36,8 @@ export const Gateway = ({
36
36
  watchdogStatus?.lifecycle === "crash_loop"
37
37
  ? "crash_loop"
38
38
  : watchdogStatus?.health;
39
+ const hasConfigurationError =
40
+ watchdogStatus?.lifecycle === "configuration_error";
39
41
  const watchdogDotClass =
40
42
  watchdogHealth === "healthy"
41
43
  ? "ac-status-dot ac-status-dot--healthy ac-status-dot--healthy-offset"
@@ -44,8 +46,11 @@ export const Gateway = ({
44
46
  : watchdogHealth === "unhealthy" || watchdogHealth === "crash_loop"
45
47
  ? "bg-red-500"
46
48
  : "bg-gray-500";
47
- const watchdogLabel =
48
- watchdogHealth === "unknown" ? "initializing" : watchdogHealth || "unknown";
49
+ const watchdogLabel = hasConfigurationError
50
+ ? "configuration error"
51
+ : watchdogHealth === "unknown"
52
+ ? "initializing"
53
+ : watchdogHealth || "unknown";
49
54
  const isRepairInProgress = repairing || !!watchdogStatus?.operationInProgress;
50
55
  const showInspectButton = watchdogHealth === "degraded" && !!onOpenWatchdog;
51
56
  const showRepairButton =
@@ -4,6 +4,7 @@ import htm from "htm";
4
4
  import {
5
5
  getModelProvider,
6
6
  getAuthProviderFromModelProvider,
7
+ getFriendlyModelLabel,
7
8
  kProviderLabels,
8
9
  kProviderOrder,
9
10
  } from "../../lib/model-config.js";
@@ -79,11 +80,13 @@ export const buildSyntheticModelEntry = (modelKey) => {
79
80
  return {
80
81
  key,
81
82
  provider,
82
- label: anthropicLabel || key,
83
+ label: anthropicLabel || getFriendlyModelLabel(key),
83
84
  };
84
85
  };
85
86
 
86
- export const getModelDisplayLabel = (model) => model?.featuredLabel || model?.label || model?.key;
87
+ export const getModelDisplayLabel = (model) =>
88
+ model?.featuredLabel ||
89
+ getFriendlyModelLabel(model?.key, model?.label || model?.key);
87
90
 
88
91
  const buildModelSearchText = (model) =>
89
92
  [
@@ -16,6 +16,7 @@ import {
16
16
  kModelCatalogCacheKey,
17
17
  kModelCatalogPollIntervalMs,
18
18
  } from "../../lib/model-catalog.js";
19
+ import { withAlwaysAvailableModels } from "../../lib/model-config.js";
19
20
 
20
21
  let kModelsTabCache = null;
21
22
  const getCredentialValue = (value) =>
@@ -199,7 +200,9 @@ export const useModels = (agentId) => {
199
200
  const addModel = useCallback(
200
201
  (modelKey) => {
201
202
  if (!modelKey) return;
202
- const catalogEntry = catalog.find((model) => model?.key === modelKey);
203
+ const catalogEntry = withAlwaysAvailableModels(catalog).find(
204
+ (model) => model?.key === modelKey,
205
+ );
203
206
  const modelConfig = catalogEntry?.agentRuntime
204
207
  ? { agentRuntime: catalogEntry.agentRuntime }
205
208
  : {};
@@ -100,7 +100,9 @@ export const NodesSetupWizard = ({
100
100
  onCopy: () =>
101
101
  copyAndToast("npm install -g openclaw", "command"),
102
102
  })}
103
- <div class="text-xs text-fg-muted">Requires Node.js 22+.</div>
103
+ <div class="text-xs text-fg-muted">
104
+ Requires Node.js ${">=22.22.3 <23, >=24.15.0 <25, or >=25.9.0."}
105
+ </div>
104
106
  `
105
107
  : null
106
108
  }
@@ -16,6 +16,7 @@ import {
16
16
  isDeprecatedOnboardingModelKey,
17
17
  getVisibleAiFieldKeys,
18
18
  kProviderAuthFields,
19
+ withAlwaysAvailableModels,
19
20
  } from "../../lib/model-config.js";
20
21
  import {
21
22
  getInitialOnboardingModelKey,
@@ -169,7 +170,9 @@ export const useWelcome = ({ onComplete }) => {
169
170
  };
170
171
 
171
172
  const applyModelCatalog = useCallback((payload) => {
172
- const list = getOnboardingModels(getModelCatalogModels(payload));
173
+ const list = getOnboardingModels(
174
+ withAlwaysAvailableModels(getModelCatalogModels(payload)),
175
+ );
173
176
  if (!payload) return;
174
177
  const isRefreshing = isModelCatalogRefreshing(payload);
175
178
  const isFallbackRefresh =
@@ -25,6 +25,10 @@ export const kFeaturedModelDefs = [
25
25
  label: "Sonnet 4.6",
26
26
  preferredKeys: ["anthropic/claude-sonnet-4-6"],
27
27
  },
28
+ {
29
+ label: "GPT-5.6 Sol",
30
+ preferredKeys: ["openai/gpt-5.6-sol", "openai-codex/gpt-5.6-sol"],
31
+ },
28
32
  {
29
33
  label: "GPT-5.5",
30
34
  preferredKeys: ["openai/gpt-5.5", "openai-codex/gpt-5.5"],
@@ -42,7 +46,22 @@ const kDeprecatedOnboardingModelKeys = new Set([
42
46
  const kCanonicalCodexOauthModelKeys = new Set([
43
47
  "openai/gpt-5.4-mini",
44
48
  "openai/gpt-5.5",
49
+ "openai/gpt-5.6-sol",
50
+ "openai/gpt-5.6-terra",
51
+ "openai/gpt-5.6-luna",
45
52
  ]);
53
+ const kFriendlyModelLabels = {
54
+ "openai/gpt-5.4-mini": "GPT-5.4 Mini",
55
+ "openai/gpt-5.5": "GPT-5.5",
56
+ "openai/gpt-5.6-sol": "GPT-5.6 Sol",
57
+ "openai/gpt-5.6-terra": "GPT-5.6 Terra",
58
+ "openai/gpt-5.6-luna": "GPT-5.6 Luna",
59
+ };
60
+
61
+ export const getFriendlyModelLabel = (modelKey, fallback = "") => {
62
+ const normalizedKey = String(modelKey || "").trim();
63
+ return kFriendlyModelLabels[normalizedKey] || fallback || normalizedKey;
64
+ };
46
65
 
47
66
  export const isDeprecatedOnboardingModelKey = (modelKey) =>
48
67
  kDeprecatedOnboardingModelKeys.has(String(modelKey || "").trim());
@@ -66,6 +85,24 @@ export const getOnboardingModelProvider = ({ modelKey, models = [] } = {}) => {
66
85
  };
67
86
 
68
87
  export const kAlwaysAvailableModelDefs = [
88
+ {
89
+ key: "openai/gpt-5.6-sol",
90
+ provider: "openai",
91
+ label: "GPT-5.6 Sol",
92
+ agentRuntime: { id: "codex" },
93
+ },
94
+ {
95
+ key: "openai/gpt-5.6-terra",
96
+ provider: "openai",
97
+ label: "GPT-5.6 Terra",
98
+ agentRuntime: { id: "codex" },
99
+ },
100
+ {
101
+ key: "openai/gpt-5.6-luna",
102
+ provider: "openai",
103
+ label: "GPT-5.6 Luna",
104
+ agentRuntime: { id: "codex" },
105
+ },
69
106
  {
70
107
  key: "openai/gpt-5.4-mini",
71
108
  provider: "openai",
@@ -82,9 +119,19 @@ export const kAlwaysAvailableModelDefs = [
82
119
 
83
120
  export const withAlwaysAvailableModels = (models = []) => {
84
121
  const merged = [...models];
85
- const keys = new Set(merged.map((model) => model?.key));
86
122
  for (const model of kAlwaysAvailableModelDefs) {
87
- if (!keys.has(model.key)) merged.push(model);
123
+ const existingIndex = merged.findIndex((candidate) => candidate?.key === model.key);
124
+ if (existingIndex < 0) {
125
+ merged.push(model);
126
+ continue;
127
+ }
128
+ const existing = merged[existingIndex];
129
+ merged[existingIndex] = {
130
+ ...model,
131
+ ...existing,
132
+ label: getFriendlyModelLabel(model.key, existing?.label || model.label),
133
+ agentRuntime: existing?.agentRuntime || model.agentRuntime,
134
+ };
88
135
  }
89
136
  return merged;
90
137
  };
@@ -8,6 +8,7 @@ const kThinkingLevelLabelOverrides = {
8
8
  adaptive: "Adaptive",
9
9
  xhigh: "Extra high",
10
10
  max: "Maximum",
11
+ ultra: "Ultra",
11
12
  };
12
13
 
13
14
  export const formatThinkingLevelLabel = (levelId = "") => {
@@ -3,6 +3,7 @@ const {
3
3
  readOpenclawConfig,
4
4
  writeOpenclawConfig,
5
5
  } = require("../openclaw-config");
6
+ const { ensureCodexRuntimePlugin } = require("../codex-runtime-config");
6
7
 
7
8
  const kDefaultAgentId = "main";
8
9
  const kAgentIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -105,6 +106,7 @@ const loadConfig = ({ fsImpl, OPENCLAW_DIR }) =>
105
106
  });
106
107
 
107
108
  const saveConfig = ({ fsImpl, OPENCLAW_DIR, config }) => {
109
+ ensureCodexRuntimePlugin(config);
108
110
  writeOpenclawConfig({
109
111
  fsModule: fsImpl,
110
112
  openclawDir: OPENCLAW_DIR,
@@ -2,6 +2,7 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { DatabaseSync } = require("node:sqlite");
4
4
  const { AUTH_PROFILES_PATH, CODEX_PROFILE_ID, OPENCLAW_DIR } = require("./constants");
5
+ const { ensureCodexRuntimePlugin } = require("./codex-runtime-config");
5
6
 
6
7
  const kDefaultAgentId = "main";
7
8
  const kApiKeyEnvVarByProvider = {
@@ -368,8 +369,13 @@ const createAuthProfiles = () => {
368
369
  const cfg = loadOpenclawConfig();
369
370
  const defaults = cfg.agents?.defaults || {};
370
371
  const configuredModels = preserveCodexRuntimeModels(defaults.models || {});
371
- if (JSON.stringify(configuredModels) !== JSON.stringify(defaults.models || {})) {
372
+ const modelsChanged =
373
+ JSON.stringify(configuredModels) !== JSON.stringify(defaults.models || {});
374
+ if (modelsChanged) {
372
375
  cfg.agents.defaults.models = configuredModels;
376
+ }
377
+ const pluginsChanged = ensureCodexRuntimePlugin(cfg);
378
+ if (modelsChanged || pluginsChanged) {
373
379
  saveOpenclawConfig(cfg);
374
380
  }
375
381
  return {
@@ -389,6 +395,7 @@ const createAuthProfiles = () => {
389
395
  if (configuredModels !== undefined) {
390
396
  cfg.agents.defaults.models = preserveCodexRuntimeModels(configuredModels);
391
397
  }
398
+ ensureCodexRuntimePlugin(cfg);
392
399
  saveOpenclawConfig(cfg);
393
400
  };
394
401
 
@@ -0,0 +1,51 @@
1
+ const {
2
+ ensurePluginsShell,
3
+ ensurePluginAllowed,
4
+ } = require("./plugin-config");
5
+
6
+ const modelUsesCodexRuntime = (modelKey, modelConfig) => {
7
+ const runtimeId = String(modelConfig?.agentRuntime?.id || "").trim();
8
+ if (runtimeId === "codex") return true;
9
+ if (runtimeId === "openclaw") return false;
10
+ return String(modelKey || "").startsWith("openai/gpt-");
11
+ };
12
+
13
+ const configUsesCodexRuntime = (cfg = {}) => {
14
+ const scopes = [
15
+ cfg.agents?.defaults || {},
16
+ ...(Array.isArray(cfg.agents?.list) ? cfg.agents.list : []),
17
+ ];
18
+ return scopes.some((scope) => {
19
+ const configuredModels = scope.models || {};
20
+ if (
21
+ Object.entries(configuredModels).some(([modelKey, modelConfig]) =>
22
+ modelUsesCodexRuntime(modelKey, modelConfig),
23
+ )
24
+ ) {
25
+ return true;
26
+ }
27
+ const primary = String(scope.model?.primary || "").trim();
28
+ return (
29
+ !!primary && modelUsesCodexRuntime(primary, configuredModels[primary])
30
+ );
31
+ });
32
+ };
33
+
34
+ const ensureCodexRuntimePlugin = (cfg = {}) => {
35
+ if (!configUsesCodexRuntime(cfg)) return false;
36
+ const before = JSON.stringify(cfg.plugins || {});
37
+ ensurePluginsShell(cfg);
38
+ ensurePluginAllowed({ cfg, pluginKey: "codex" });
39
+ const existingEntry = cfg.plugins.entries.codex;
40
+ cfg.plugins.entries.codex = {
41
+ ...(existingEntry && typeof existingEntry === "object" ? existingEntry : {}),
42
+ enabled: true,
43
+ };
44
+ return JSON.stringify(cfg.plugins) !== before;
45
+ };
46
+
47
+ module.exports = {
48
+ configUsesCodexRuntime,
49
+ ensureCodexRuntimePlugin,
50
+ modelUsesCodexRuntime,
51
+ };
@@ -172,6 +172,24 @@ const kMinimalFallbackOnboardingModels = [
172
172
  provider: "anthropic",
173
173
  label: "Claude Haiku 4.6",
174
174
  },
175
+ {
176
+ key: "openai/gpt-5.6-sol",
177
+ provider: "openai",
178
+ label: "GPT-5.6 Sol",
179
+ agentRuntime: { id: "codex" },
180
+ },
181
+ {
182
+ key: "openai/gpt-5.6-terra",
183
+ provider: "openai",
184
+ label: "GPT-5.6 Terra",
185
+ agentRuntime: { id: "codex" },
186
+ },
187
+ {
188
+ key: "openai/gpt-5.6-luna",
189
+ provider: "openai",
190
+ label: "GPT-5.6 Luna",
191
+ agentRuntime: { id: "codex" },
192
+ },
175
193
  {
176
194
  key: "openai/gpt-5.5",
177
195
  provider: "openai",
@@ -48,6 +48,24 @@ const kGlobalModelPricing = {
48
48
  "claude-haiku-4-6": { input: 0.8, output: 4.0 },
49
49
  "gpt-5": { input: 1.25, output: 10.0 },
50
50
  "gpt-5.4": { input: 2.5, output: 10.0 },
51
+ "gpt-5.6-sol": {
52
+ input: 5.0,
53
+ output: 30.0,
54
+ cacheRead: 0.5,
55
+ cacheWrite: 6.25,
56
+ },
57
+ "gpt-5.6-terra": {
58
+ input: 2.5,
59
+ output: 15.0,
60
+ cacheRead: 0.25,
61
+ cacheWrite: 3.125,
62
+ },
63
+ "gpt-5.6-luna": {
64
+ input: 1.0,
65
+ output: 6.0,
66
+ cacheRead: 0.1,
67
+ cacheWrite: 1.25,
68
+ },
51
69
  "gpt-5.1-codex": { input: 2.5, output: 10.0 },
52
70
  "gpt-5.3-codex": { input: 2.5, output: 10.0 },
53
71
  "gpt-4.1": { input: 2.0, output: 8.0 },
@@ -123,13 +123,29 @@ const normalizeOnboardingModels = (models) => {
123
123
  const provider = resolveModelProvider(key);
124
124
  if (!kOnboardingModelProviders.has(provider)) continue;
125
125
  if (!deduped.has(key)) {
126
+ const agentRuntime = isCodexRuntimeModel
127
+ ? { id: "codex" }
128
+ : model.agentRuntime;
126
129
  deduped.set(key, {
127
130
  key,
128
131
  provider,
129
132
  label: model.name || model.key,
130
- ...(isCodexRuntimeModel
131
- ? { agentRuntime: { id: "codex" } }
133
+ ...(typeof model.available === "boolean"
134
+ ? { available: model.available }
132
135
  : {}),
136
+ ...(typeof model.reasoning === "boolean"
137
+ ? { reasoning: model.reasoning }
138
+ : {}),
139
+ ...(Number.isFinite(model.contextWindow)
140
+ ? { contextWindow: model.contextWindow }
141
+ : {}),
142
+ ...(Number.isFinite(model.maxTokens)
143
+ ? { maxTokens: model.maxTokens }
144
+ : {}),
145
+ ...(model.compat && typeof model.compat === "object"
146
+ ? { compat: model.compat }
147
+ : {}),
148
+ ...(agentRuntime ? { agentRuntime } : {}),
133
149
  });
134
150
  }
135
151
  }
@@ -14,6 +14,7 @@ const { registerWebhookRoutes } = require("../routes/webhooks");
14
14
  const { registerWatchdogRoutes } = require("../routes/watchdog");
15
15
  const { registerUsageRoutes } = require("../routes/usage");
16
16
  const { registerGmailRoutes } = require("../routes/gmail");
17
+ const { ensureWebhookMappingIds } = require("../webhooks");
17
18
  const { registerDoctorRoutes } = require("../routes/doctor");
18
19
  const { registerAgentRoutes } = require("../routes/agents");
19
20
  const { registerCronRoutes } = require("../routes/cron");
@@ -178,6 +179,7 @@ const registerServerRoutes = ({
178
179
  runOnboardedBootSequence({
179
180
  ensureManagedExecDefaults,
180
181
  ensureUsageTrackerPluginConfig,
182
+ ensureWebhookMappingIds: () => ensureWebhookMappingIds({ fs, constants }),
181
183
  doSyncPromptFiles,
182
184
  reloadEnv,
183
185
  syncChannelConfig,
@@ -3,7 +3,7 @@ const path = require("path");
3
3
  const { ALPHACLAW_DIR, kFallbackOnboardingModels } = require("./constants");
4
4
  const { getCommandOutputCandidates } = require("./utils/command-output");
5
5
 
6
- const kModelCatalogCacheVersion = 1;
6
+ const kModelCatalogCacheVersion = 2;
7
7
  const kModelCatalogRefreshBackoffMs = 30 * 1000;
8
8
  const kModelCatalogLoadTimeoutMs = 120 * 1000;
9
9
  const kModelCatalogBootstrapSource = "bootstrap";
@@ -36,9 +36,9 @@ const normalizeCachedModels = ({
36
36
  } = {}) =>
37
37
  normalizeOnboardingModels(
38
38
  (Array.isArray(models) ? models : []).map((model) => ({
39
+ ...model,
39
40
  key: model?.key,
40
41
  name: model?.label || model?.name || model?.key,
41
- ...(model?.agentRuntime ? { agentRuntime: model.agentRuntime } : {}),
42
42
  })),
43
43
  );
44
44
 
@@ -5,6 +5,7 @@ const {
5
5
  ensureUsageTrackerPluginEntry,
6
6
  } = require("../usage-tracker-config");
7
7
  const { isOpenAiCompatApiEnabled } = require("../alphaclaw-config");
8
+ const { ensureCodexRuntimePlugin } = require("../codex-runtime-config");
8
9
 
9
10
  const kDefaultToolsProfile = "full";
10
11
  const kBootstrapExtraFiles = [
@@ -248,6 +249,7 @@ const writeSanitizedOpenclawConfig = ({
248
249
  cfg,
249
250
  varMap,
250
251
  });
252
+ ensureCodexRuntimePlugin(cfg);
251
253
 
252
254
  let content = JSON.stringify(cfg, null, 2);
253
255
  const replacements = buildSecretReplacements(varMap, process.env);
@@ -357,6 +359,8 @@ const writeManagedImportOpenclawConfig = ({
357
359
  ensurePluginAllowed({ cfg, pluginKey: "whatsapp" });
358
360
  }
359
361
 
362
+ ensureCodexRuntimePlugin(cfg);
363
+
360
364
  fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
361
365
  };
362
366
 
@@ -5,6 +5,9 @@ const kAnthropicApiKeyPrefix = "sk-ant-api";
5
5
  const kCanonicalCodexOauthModelKeys = new Set([
6
6
  "openai/gpt-5.4-mini",
7
7
  "openai/gpt-5.5",
8
+ "openai/gpt-5.6-sol",
9
+ "openai/gpt-5.6-terra",
10
+ "openai/gpt-5.6-luna",
8
11
  ]);
9
12
 
10
13
  const usesCodexOauth = (modelKey, provider) =>
@@ -3,14 +3,36 @@ const path = require("path");
3
3
  const { pathToFileURL } = require("url");
4
4
  const { DatabaseSync } = require("node:sqlite");
5
5
 
6
- const findOpenclawDistModule = (prefix) => {
6
+ const findOpenclawDistModules = (prefix) => {
7
7
  const entryPath = require.resolve("openclaw");
8
8
  const distDir = path.dirname(entryPath);
9
- const filename = fs
9
+ const filenames = fs
10
10
  .readdirSync(distDir)
11
- .find((name) => name.startsWith(`${prefix}-`) && name.endsWith(".js"));
12
- if (!filename) throw new Error(`OpenClaw migration module not found: ${prefix}`);
13
- return path.join(distDir, filename);
11
+ .filter((name) => name.startsWith(`${prefix}-`) && name.endsWith(".js"));
12
+ if (filenames.length === 0) {
13
+ throw new Error(`OpenClaw migration module not found: ${prefix}`);
14
+ }
15
+ return filenames.map((filename) => path.join(distDir, filename));
16
+ };
17
+
18
+ const loadOpenclawMigrationApi = async ({ prefix, functionNames }) => {
19
+ const api = {};
20
+ for (const modulePath of findOpenclawDistModules(prefix)) {
21
+ const mod = await import(pathToFileURL(modulePath).href);
22
+ for (const candidate of Object.values(mod)) {
23
+ if (typeof candidate !== "function") continue;
24
+ if (functionNames.includes(candidate.name) && !api[candidate.name]) {
25
+ api[candidate.name] = candidate;
26
+ }
27
+ }
28
+ }
29
+ const missing = functionNames.filter((name) => typeof api[name] !== "function");
30
+ if (missing.length > 0) {
31
+ throw new Error(
32
+ `OpenClaw migration exports not found for ${prefix}: ${missing.join(", ")}`,
33
+ );
34
+ }
35
+ return api;
14
36
  };
15
37
 
16
38
  const writeConfig = (configPath, cfg) => {
@@ -73,33 +95,55 @@ const migrateLegacyCodexState = async ({
73
95
  }
74
96
 
75
97
  const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
76
- const routeModule = await import(
77
- pathToFileURL(findOpenclawDistModule("codex-route-warnings")).href
78
- );
79
- const authModule = await import(
80
- pathToFileURL(findOpenclawDistModule("doctor-auth-flat-profiles")).href
81
- );
98
+ const routeApi = await loadOpenclawMigrationApi({
99
+ prefix: "codex-route-warnings",
100
+ functionNames: [
101
+ "maybeRepairCodexRoutes",
102
+ "maybeRepairCodexSessionRoutes",
103
+ ],
104
+ });
105
+ const authApi = await loadOpenclawMigrationApi({
106
+ prefix: "doctor-auth-flat-profiles",
107
+ functionNames: [
108
+ "collectOpenAICodexAuthProfileStoreIdMap",
109
+ "maybeRepairOpenAICodexAuthConfig",
110
+ "maybeRepairOpenAICodexAuthProfileStores",
111
+ "maybeMigrateAuthProfileJsonStoresToSqlite",
112
+ ],
113
+ });
82
114
 
83
115
  const changes = [];
84
116
  const warnings = [];
85
- const routeRepair = routeModule.r({ cfg, env, shouldRepair: true });
117
+ const routeRepair = routeApi.maybeRepairCodexRoutes({
118
+ cfg,
119
+ env,
120
+ shouldRepair: true,
121
+ });
86
122
  let nextCfg = routeRepair.cfg;
87
123
  changes.push(...routeRepair.changes);
88
124
  warnings.push(...routeRepair.warnings);
89
125
 
90
- const profileIdMap = authModule.t({ cfg: nextCfg, env });
91
- const configAuthRepair = authModule.a(nextCfg, { profileIdMap });
126
+ const profileIdMap = authApi.collectOpenAICodexAuthProfileStoreIdMap({
127
+ cfg: nextCfg,
128
+ env,
129
+ });
130
+ const configAuthRepair = authApi.maybeRepairOpenAICodexAuthConfig(nextCfg, {
131
+ profileIdMap,
132
+ });
92
133
  nextCfg = configAuthRepair.config;
93
134
  changes.push(...configAuthRepair.changes);
94
135
  warnings.push(...configAuthRepair.warnings);
95
136
 
96
137
  if (changes.length > 0) writeConfig(configPath, nextCfg);
97
138
 
98
- const storeRepair = await authModule.o({ cfg: nextCfg, env });
139
+ const storeRepair = await authApi.maybeRepairOpenAICodexAuthProfileStores({
140
+ cfg: nextCfg,
141
+ env,
142
+ });
99
143
  changes.push(...storeRepair.changes);
100
144
  warnings.push(...storeRepair.warnings);
101
145
 
102
- const sqliteMigration = await authModule.n({
146
+ const sqliteMigration = await authApi.maybeMigrateAuthProfileJsonStoresToSqlite({
103
147
  cfg: nextCfg,
104
148
  env,
105
149
  prompter: { confirmAutoFix: async () => true },
@@ -113,7 +157,7 @@ const migrateLegacyCodexState = async ({
113
157
  writeConfig(configPath, nextCfg);
114
158
  }
115
159
 
116
- const sessionRepair = await routeModule.i({
160
+ const sessionRepair = await routeApi.maybeRepairCodexSessionRoutes({
117
161
  cfg: nextCfg,
118
162
  env,
119
163
  shouldRepair: true,
@@ -27,29 +27,28 @@ const loadThinkingModule = async () => {
27
27
  return thinkingModulePromise;
28
28
  };
29
29
 
30
- const isNormalizeThinkLevel = (candidate) => {
31
- if (typeof candidate !== "function") return false;
32
- try {
33
- return (
34
- candidate("off") === "off" &&
35
- candidate("low") === "low" &&
36
- candidate("high") === "high"
37
- );
38
- } catch {
39
- return false;
30
+ const normalizeThinkingLevel = (raw) => {
31
+ const key = String(raw || "").trim().toLowerCase();
32
+ if (!key) return null;
33
+ const collapsed = key.replace(/[\s_-]+/g, "");
34
+ if (collapsed === "adaptive" || collapsed === "auto") return "adaptive";
35
+ if (collapsed === "max") return "max";
36
+ if (collapsed === "ultra") return "ultra";
37
+ if (collapsed === "xhigh" || collapsed === "extrahigh") return "xhigh";
38
+ if (key === "off") return "off";
39
+ if (["on", "enable", "enabled"].includes(key)) return "low";
40
+ if (["min", "minimal"].includes(key)) return "minimal";
41
+ if (["low", "thinkhard", "think-hard", "think_hard"].includes(key)) {
42
+ return "low";
40
43
  }
41
- };
42
-
43
- const resolveNormalizeThinkLevel = (mod) => {
44
- const candidates = [
45
- mod.normalizeThinkLevel,
46
- mod.f,
47
- mod.p,
48
- ...Object.values(mod),
49
- ];
50
- const match = candidates.find(isNormalizeThinkLevel);
51
- if (!match) throw new Error("OpenClaw normalizeThinkLevel export not found");
52
- return match;
44
+ if (["mid", "med", "medium", "thinkharder", "think-harder", "harder"].includes(key)) {
45
+ return "medium";
46
+ }
47
+ if (["high", "ultrathink", "thinkhardest", "highest"].includes(key)) {
48
+ return "high";
49
+ }
50
+ if (key === "think") return "minimal";
51
+ return null;
53
52
  };
54
53
 
55
54
  const splitModelKey = (modelKey = "") => {
@@ -80,13 +79,13 @@ const resolveThinkingApi = async () => {
80
79
  return {
81
80
  listThinkingLevelOptions: mod.listThinkingLevelOptions || mod.i,
82
81
  resolveThinkingDefaultForModel: mod.resolveThinkingDefaultForModel || mod.s,
83
- normalizeThinkLevel: resolveNormalizeThinkLevel(mod),
84
82
  };
85
83
  };
86
84
 
87
85
  const resolveThinkingOptionsForModel = async ({
88
86
  modelKey = "",
89
87
  catalog = [],
88
+ agentRuntime = "",
90
89
  } = {}) => {
91
90
  const { provider, model } = splitModelKey(modelKey);
92
91
  if (!provider || !model) {
@@ -96,12 +95,20 @@ const resolveThinkingOptionsForModel = async ({
96
95
  };
97
96
  }
98
97
  const api = await resolveThinkingApi();
99
- const levels = api.listThinkingLevelOptions(provider, model, catalog) || [];
98
+ const normalizedAgentRuntime = String(agentRuntime || "").trim() || undefined;
99
+ const levels =
100
+ api.listThinkingLevelOptions(
101
+ provider,
102
+ model,
103
+ catalog,
104
+ normalizedAgentRuntime,
105
+ ) || [];
100
106
  const modelDefault =
101
107
  api.resolveThinkingDefaultForModel({
102
108
  provider,
103
109
  model,
104
110
  catalog,
111
+ agentRuntime: normalizedAgentRuntime,
105
112
  }) || "off";
106
113
  return {
107
114
  levels: levels.map((entry) => ({
@@ -114,14 +121,13 @@ const resolveThinkingOptionsForModel = async ({
114
121
 
115
122
  const normalizeThinkingDefaultValue = async (raw) => {
116
123
  if (raw === null || raw === undefined || raw === "") return null;
117
- const api = await resolveThinkingApi();
118
- const normalized = api.normalizeThinkLevel(String(raw || "").trim());
119
- return normalized || null;
124
+ return normalizeThinkingLevel(raw);
120
125
  };
121
126
 
122
127
  module.exports = {
123
128
  buildCatalogEntry,
124
129
  loadThinkingModule,
130
+ normalizeThinkingLevel,
125
131
  normalizeThinkingDefaultValue,
126
132
  resolveThinkingOptionsForModel,
127
133
  splitModelKey,