sneakoscope 4.7.0 → 4.7.1

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.
@@ -1666,14 +1666,12 @@ export function normalizeCodexFastModeUiConfig(text = '', opts = {}) {
1666
1666
  return normalizeCodexFastModeUiConfigOnce(normalizeCodexFastModeUiConfigOnce(text, opts), opts);
1667
1667
  }
1668
1668
  function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
1669
- // Preserve user-owned top-level scalars (model / service_tier / model_reasoning_effort):
1670
- // SKS only supplies a default when the user has not chosen one, and never strips the
1671
- // user's own reasoning effort. SKS continues to manage its own namespaced tables below
1672
- // ([features], [profiles.sks-*], [user.fast_mode], [plugins]).
1669
+ // Keep model and reasoning selection out of top-level config so Codex Desktop can
1670
+ // expose its native model/speed selectors. SKS-owned defaults live in profiles below.
1673
1671
  let next = String(text || '');
1672
+ next = removeLegacyTopLevelCodexModeLocks(next);
1674
1673
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
1675
1674
  next = removeTomlTableKey(next, 'features', 'codex_hooks');
1676
- next = upsertTopLevelTomlStringIfAbsent(next, 'model', 'gpt-5.5');
1677
1675
  if (opts.forceFastMode === true) {
1678
1676
  next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
1679
1677
  }
@@ -1739,7 +1737,7 @@ function removeLegacyTopLevelCodexModeLocks(text = '') {
1739
1737
  return lines.filter((line, index) => {
1740
1738
  if (index >= end)
1741
1739
  return true;
1742
- return !/^\s*model_reasoning_effort\s*=/.test(line);
1740
+ return !/^\s*(?:model|model_reasoning_effort)\s*=/.test(line);
1743
1741
  }).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
1744
1742
  }
1745
1743
  function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
@@ -3118,8 +3116,8 @@ export async function selftestCodexLb(tmp) {
3118
3116
  });
3119
3117
  if (brokenChain.ok || brokenChain.status !== 'previous_response_not_found' || brokenChain.chain_unhealthy !== true)
3120
3118
  throw new Error('selftest: codex-lb response chain health check did not detect previous_response_not_found');
3121
- if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('hooks = true') || hasDeprecatedCodexHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('remote_control = true') || !codexLbConfig.includes('multi_agent = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('codex_git_commit = true') || !codexLbConfig.includes('computer_use = true') || !codexLbConfig.includes('browser_use = true') || !codexLbConfig.includes('browser_use_external = true') || !codexLbConfig.includes('guardian_approval = true') || !codexLbConfig.includes('tool_suggest = true') || !codexLbConfig.includes('apps = true') || !codexLbConfig.includes('plugins = true') || !codexLbConfig.includes('[plugins."latex@openai-bundled"]') || !codexLbConfig.includes('[plugins."documents@openai-primary-runtime"]') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !/\[profiles\.custom\][\s\S]*?model_reasoning_effort = "low"/.test(codexLbConfig) || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig))
3122
- throw new Error('selftest: codex-lb setup did not preserve Codex App feature flags, default plugins, profile-scoped reasoning effort, explicit Fast profile, Codex Git commit generation, force GPT-5.5, or migrate the hooks feature flag');
3119
+ if (!codexLbConfig.includes('hooks = true') || hasDeprecatedCodexHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('remote_control = true') || !codexLbConfig.includes('multi_agent = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('codex_git_commit = true') || !codexLbConfig.includes('computer_use = true') || !codexLbConfig.includes('browser_use = true') || !codexLbConfig.includes('browser_use_external = true') || !codexLbConfig.includes('guardian_approval = true') || !codexLbConfig.includes('tool_suggest = true') || !codexLbConfig.includes('apps = true') || !codexLbConfig.includes('plugins = true') || !codexLbConfig.includes('[plugins."latex@openai-bundled"]') || !codexLbConfig.includes('[plugins."documents@openai-primary-runtime"]') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !/\[profiles\.custom\][\s\S]*?model_reasoning_effort = "low"/.test(codexLbConfig) || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig))
3120
+ throw new Error('selftest: codex-lb setup did not preserve Codex App feature flags, default plugins, profile-scoped reasoning effort, explicit Fast profile, Codex Git commit generation, or migrate the hooks feature flag');
3123
3121
  if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig))
3124
3122
  throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
3125
3123
  const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex '--model' 'gpt-5.5'`;
@@ -3135,7 +3133,7 @@ function hasTopLevelCodexModeLock(text = '') {
3135
3133
  const lines = String(text || '').split('\n');
3136
3134
  const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
3137
3135
  const top = (firstTable === -1 ? lines : lines.slice(0, firstTable)).join('\n');
3138
- return /(^|\n)\s*model_reasoning_effort\s*=/.test(top);
3136
+ return /(^|\n)\s*(?:model|model_reasoning_effort)\s*=/.test(top);
3139
3137
  }
3140
3138
  function hasDeprecatedCodexHooksFeatureFlag(text = '') {
3141
3139
  const lines = String(text || '').split('\n');
@@ -56,7 +56,7 @@ async function runDoctor(args = [], root, doctorFix) {
56
56
  const actualCodexProbeRequested = flag(args, '--actual-codex') || flag(args, '--require-actual-codex') || Boolean(codexBin);
57
57
  const actualCodexProbeEnabled = deepDiagnostics || actualCodexProbeRequested;
58
58
  const requireActualCodexProbe = flag(args, '--require-actual-codex') || (deepDiagnostics && doctorFix);
59
- const shouldEvaluateCodexAppUiRepair = deepDiagnostics || flag(args, '--repair-codex-app-ui');
59
+ const shouldEvaluateCodexAppUiRepair = doctorFix || deepDiagnostics || flag(args, '--repair-codex-app-ui');
60
60
  const shouldRunZellijRepair = deepDiagnostics || flag(args, '--repair-zellij') || flag(args, '--install-homebrew') || process.env.SKS_REQUIRE_ZELLIJ === '1';
61
61
  const nativeCapabilityDiagnosticsRequested = deepDiagnostics || flag(args, '--repair-native-capabilities');
62
62
  const doctorPhaseIds = doctorPhaseIdsForProfile(doctorProfile);
@@ -4,7 +4,8 @@ import { ensureDir, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic }
4
4
  import { codexHome, scanProjectLocalForbiddenKeys, snapshotCodexAppUiState } from './codex-app-ui-state-snapshot.js';
5
5
  import { assertCodexAppUiMutationAllowed } from './codex-app-ui-clobber-guard.js';
6
6
  export const CODEX_APP_FAST_UI_REPAIR_SCHEMA = 'sks.codex-app-fast-ui-repair.v1';
7
- const FAST_UI_TOP_LEVEL_RE = /^\s*service_tier\s*=/;
7
+ const FAST_UI_TOP_LEVEL_RE = /^\s*(?:model|model_reasoning_effort|service_tier)\s*=/;
8
+ const CODEX_APP_MODE_LOCK_TOP_LEVEL_RE = /^\s*(?:model|model_reasoning_effort)\s*=/;
8
9
  const FAST_UI_FEATURE_LINE_RE = /^\s*fast_mode\s*=/;
9
10
  const FAST_UI_USER_TABLE_LINE_RE = /^\s*(enabled|visible|locked|hidden|disabled)\s*=/;
10
11
  const SKS_CAUSED_RE = /(?:SKS|Sneakoscope|codex-lb|sks-mad|sks fast)/i;
@@ -125,10 +126,11 @@ function stripProjectLocalForbiddenKeys(text) {
125
126
  }
126
127
  function stripSksCausedHostOwnedLines(text) {
127
128
  return stripMatchingLines(text, (line, table, previous, next) => {
129
+ const isCodexAppModeLock = !table && CODEX_APP_MODE_LOCK_TOP_LEVEL_RE.test(line);
128
130
  const isFastUiLine = FAST_UI_TOP_LEVEL_RE.test(line)
129
131
  || (table === 'features' && FAST_UI_FEATURE_LINE_RE.test(line))
130
132
  || (table === 'user.fast_mode' && FAST_UI_USER_TABLE_LINE_RE.test(line));
131
- return isFastUiLine && (SKS_CAUSED_RE.test(line) || SKS_CAUSED_RE.test(previous) || SKS_CAUSED_RE.test(next));
133
+ return isCodexAppModeLock || (isFastUiLine && (SKS_CAUSED_RE.test(line) || SKS_CAUSED_RE.test(previous) || SKS_CAUSED_RE.test(next)));
132
134
  });
133
135
  }
134
136
  function stripMatchingLines(text, shouldRemove) {
@@ -4,6 +4,8 @@ import path from 'node:path';
4
4
  import { exists, nowIso, readText, sha256, writeJsonAtomic } from '../fsx.js';
5
5
  export const CODEX_APP_UI_STATE_SNAPSHOT_SCHEMA = 'sks.codex-app-ui-state-snapshot.v1';
6
6
  export const PROJECT_LOCAL_FORBIDDEN_CODEX_KEYS = [
7
+ 'model',
8
+ 'model_reasoning_effort',
7
9
  'openai_base_url',
8
10
  'chatgpt_base_url',
9
11
  'apps_mcp_product_sku',
@@ -15,7 +17,7 @@ export const PROJECT_LOCAL_FORBIDDEN_CODEX_KEYS = [
15
17
  'experimental_realtime_ws_base_url',
16
18
  'otel'
17
19
  ];
18
- const HOST_OWNED_KEY_RE = /^(?:openai_base_url|chatgpt_base_url|apps_mcp_product_sku|model_provider|model_providers(?:\.|$)|notify|profile|profiles(?:\.|$)|experimental_realtime_ws_base_url|otel(?:\.|$)|features\.fast_mode|service_tier|user\.fast_mode(?:\.|$))/;
20
+ const HOST_OWNED_KEY_RE = /^(?:model|model_reasoning_effort|openai_base_url|chatgpt_base_url|apps_mcp_product_sku|model_provider|model_providers(?:\.|$)|notify|profile|profiles(?:\.|$)|experimental_realtime_ws_base_url|otel(?:\.|$)|features\.fast_mode|service_tier|user\.fast_mode(?:\.|$))/;
19
21
  const SECRET_KEY_RE = /(?:key|token|secret|password|credential|cookie|authorization|bearer|refresh|access)/i;
20
22
  export function codexHome(input) {
21
23
  return path.resolve(String(input?.codexHome || process.env.CODEX_HOME || path.join(os.homedir(), '.codex')));
@@ -35,11 +37,16 @@ export async function snapshotCodexAppUiState(root = process.cwd(), input = {})
35
37
  const providerSignals = hostOwnedSignals
36
38
  .filter((signal) => signal.provider_related)
37
39
  .map((signal) => `${signal.key_path}=${signal.value_preview}`);
38
- const fastSelectorLocked = hostOwnedSignals.some((signal) => {
40
+ const baseConfigHostOwnedSignals = files
41
+ .filter((file) => !isProfileConfigSnapshotPath(file.path))
42
+ .flatMap((file) => file.signals.filter((signal) => signal.host_owned));
43
+ const fastSelectorLocked = baseConfigHostOwnedSignals.some((signal) => {
39
44
  if (signal.key_path === 'features.fast_mode' && signal.value_preview === 'false')
40
45
  return true;
41
46
  if (signal.key_path.startsWith('user.fast_mode') && /hidden|fixed|disabled|false/i.test(signal.value_preview))
42
47
  return true;
48
+ if (signal.key_path === 'model' || signal.key_path === 'model_reasoning_effort')
49
+ return true;
43
50
  if (signal.key_path === 'service_tier' && signal.sks_related)
44
51
  return true;
45
52
  return false;
@@ -120,7 +127,7 @@ export function scanTomlSignals(text) {
120
127
  value_hash: sha256(value),
121
128
  line: index + 1,
122
129
  host_owned: HOST_OWNED_KEY_RE.test(keyPath),
123
- fast_ui_related: /(?:fast_mode|service_tier)/i.test(keyPath) || /(?:fast|priority|default)/i.test(value),
130
+ fast_ui_related: /(?:fast_mode|service_tier|model_reasoning_effort|^model$)/i.test(keyPath) || /(?:fast|priority|default)/i.test(value),
124
131
  provider_related: /(?:provider|base_url|auth|profile|openai|chatgpt|codex-lb)/i.test(lowerPath),
125
132
  sks_related: /(?:sks|sneakoscope|codex-lb)/i.test(lineText)
126
133
  });
@@ -176,6 +183,10 @@ async function profileConfigFiles(home) {
176
183
  return [];
177
184
  }
178
185
  }
186
+ function isProfileConfigSnapshotPath(file) {
187
+ const base = path.basename(file);
188
+ return /^profile-.+\.config\.toml$/.test(base) || /^[A-Za-z0-9_-]+\.config\.toml$/.test(base);
189
+ }
179
190
  async function readAuthMetadata(file) {
180
191
  const text = await readText(file, null);
181
192
  if (text == null)
@@ -798,6 +798,8 @@ async function codexFastModeConfigStatus(opts = {}) {
798
798
  if (!config.text)
799
799
  continue;
800
800
  const topLevel = topLevelToml(config.text);
801
+ if (/(^|\n)\s*model\s*=/.test(topLevel))
802
+ blockers.push(`${config.scope}:top_level_model`);
801
803
  if (/(^|\n)\s*model_reasoning_effort\s*=/.test(topLevel))
802
804
  blockers.push(`${config.scope}:top_level_model_reasoning_effort`);
803
805
  if (/(^|\n)\s*fast_default_opt_out\s*=\s*true\s*(?:#.*)?(?=\n|$)/.test(tomlTable(config.text, 'notice')))
package/dist/core/init.js CHANGED
@@ -69,8 +69,7 @@ export function hasTopLevelCodexModeLock(text = '') {
69
69
  const lines = String(text || '').split('\n');
70
70
  const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
71
71
  const top = (firstTable === -1 ? lines : lines.slice(0, firstTable)).join('\n');
72
- const model = top.match(/^model\s*=\s*"([^"]+)"/m)?.[1];
73
- return (Boolean(model) && model !== 'gpt-5.5') || /^model_reasoning_effort\s*=/m.test(top);
72
+ return /^model\s*=/.test(top) || /^model_reasoning_effort\s*=/m.test(top);
74
73
  }
75
74
  export function hasDeprecatedCodexHooksFeatureFlag(text = '') {
76
75
  const lines = String(text || '').split('\n');
@@ -590,10 +589,8 @@ export async function initProject(root, opts = {}) {
590
589
  let next = removeLegacyTopLevelCodexModeLocks(String(existingContent || '').trimEnd());
591
590
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
592
591
  next = removeTomlTableKey(next, 'features', 'codex_hooks');
593
- // User-overridable top-level keys: seed defaults only when ABSENT so an
594
- // upgrade never clobbers a user's chosen model/service_tier. model_reasoning_effort
595
- // is intentionally left untouched at the top level.
596
- next = upsertTopLevelTomlStringIfAbsent(next, 'model', 'gpt-5.5');
592
+ // Model/reasoning defaults live in profiles. Leaving them out of top-level config
593
+ // keeps Codex Desktop's native model and speed selectors available.
597
594
  next = upsertTopLevelTomlBooleanIfAbsent(next, 'suppress_unstable_features_warning', true);
598
595
  // Codex App feature flags: SET-IF-ABSENT only (see note above).
599
596
  for (const flag of MANAGED_CODEX_FEATURE_FLAGS) {
@@ -704,7 +701,7 @@ export async function initProject(root, opts = {}) {
704
701
  return lines.filter((line, index) => {
705
702
  if (index >= end)
706
703
  return true;
707
- return !/^\s*model_reasoning_effort\s*=/.test(line);
704
+ return !/^\s*(?:model|model_reasoning_effort)\s*=/.test(line);
708
705
  }).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
709
706
  }
710
707
  function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
@@ -7,8 +7,8 @@ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-doctor-fast-ui-'));
7
7
  const codexHome = path.join(root, 'home', '.codex');
8
8
  await fs.mkdir(path.join(root, '.codex'), { recursive: true });
9
9
  await fs.mkdir(codexHome, { recursive: true });
10
- await fs.writeFile(path.join(root, '.codex', 'config.toml'), 'model_provider = "codex-lb"\n');
11
- await fs.writeFile(path.join(codexHome, 'config.toml'), '# SKS forced fast UI during legacy install\nmodel_provider = "codex-lb"\nservice_tier = "fast"\n[features]\nfast_mode = false # user disabled, must remain untouched\n\n[profiles.sks-fast-high]\nmodel = "gpt-5.5"\nservice_tier = "fast"\n\n[model_providers.codex-lb]\nname = "OpenAI"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = false\n');
10
+ await fs.writeFile(path.join(root, '.codex', 'config.toml'), 'model = "gpt-5.5"\nmodel_reasoning_effort = "medium"\nmodel_provider = "codex-lb"\n');
11
+ await fs.writeFile(path.join(codexHome, 'config.toml'), '# SKS forced fast UI during legacy install\nmodel = "z-ai/glm-5.2"\nmodel_reasoning_effort = "xhigh"\nmodel_provider = "codex-lb"\nservice_tier = "fast"\n[features]\nfast_mode = false # user disabled, must remain untouched\n\n[profiles.sks-fast-high]\nmodel = "gpt-5.5"\nservice_tier = "fast"\n\n[model_providers.codex-lb]\nname = "OpenAI"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = false\n');
12
12
  const plan = await repairCodexAppFastUi(root, { codexHome, apply: false });
13
13
  const repaired = await repairCodexAppFastUi(root, { codexHome, apply: true });
14
14
  const projectAfter = await fs.readFile(path.join(root, '.codex', 'config.toml'), 'utf8');
@@ -26,6 +26,10 @@ const ok = plan.fast_selector === 'manual_action_required'
26
26
  && repaired.fast_selector === 'repaired'
27
27
  && repaired.safe_auto_apply === true
28
28
  && backups.length >= 2
29
+ && !/^model\s*=/m.test(projectAfter.split(/\n\s*\[/)[0] || '')
30
+ && !/^model_reasoning_effort\s*=/m.test(projectAfter.split(/\n\s*\[/)[0] || '')
31
+ && !/^model\s*=/m.test(homeTopLevel)
32
+ && !/^model_reasoning_effort\s*=/m.test(homeTopLevel)
29
33
  && !/model_provider\s*=/.test(projectAfter)
30
34
  && !/service_tier\s*=/.test(homeTopLevel)
31
35
  && /fast_mode = false/.test(homeAfter)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "4.7.0",
4
+ "version": "4.7.1",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",