sneakoscope 6.0.2 → 6.0.3

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.
Files changed (49) hide show
  1. package/README.md +3 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +5 -0
  5. package/dist/bin/sks-dispatch.js +2 -0
  6. package/dist/cli/install-helpers.js +41 -6
  7. package/dist/commands/codex-app.js +8 -3
  8. package/dist/commands/codex-lb.js +37 -12
  9. package/dist/config/skills-manifest.json +57 -57
  10. package/dist/core/agents/agent-effort-policy.js +46 -33
  11. package/dist/core/agents/agent-orchestrator.js +1 -1
  12. package/dist/core/agents/agent-proof-evidence.js +16 -2
  13. package/dist/core/agents/agent-roster.js +2 -2
  14. package/dist/core/agents/native-cli-session-swarm.js +18 -1
  15. package/dist/core/agents/native-worker-backend-router.js +179 -108
  16. package/dist/core/agents/parallel-write-fixture.js +15 -10
  17. package/dist/core/agents/runtime-proof-summary.js +21 -3
  18. package/dist/core/codex-app/codex-app-restart.js +38 -8
  19. package/dist/core/codex-app/sks-menubar.js +92 -37
  20. package/dist/core/codex-app.js +26 -4
  21. package/dist/core/codex-control/codex-model-metadata.js +8 -4
  22. package/dist/core/codex-lb/codex-lb-env.js +78 -0
  23. package/dist/core/commands/basic-cli.js +15 -0
  24. package/dist/core/commands/naruto-command.js +37 -9
  25. package/dist/core/naruto/naruto-write-e2e.js +3 -4
  26. package/dist/core/provider/model-router.js +64 -3
  27. package/dist/core/release/package-size-budget.js +2 -0
  28. package/dist/core/routes/constants.js +1 -1
  29. package/dist/core/routes/model-mode-router.js +3 -0
  30. package/dist/core/routes.js +7 -9
  31. package/dist/core/update/update-migration-state.js +1 -1
  32. package/dist/core/update/update-notice.js +23 -0
  33. package/dist/core/update-check.js +46 -10
  34. package/dist/core/version.js +1 -1
  35. package/dist/scripts/codex-app-provider-model-ui-check.js +17 -2
  36. package/dist/scripts/doctor-fix-proves-codex-read-check.js +1 -1
  37. package/dist/scripts/gate-policy-audit-check.js +2 -2
  38. package/dist/scripts/legacy-upgrade-matrix-check.js +1 -1
  39. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +2 -1
  40. package/dist/scripts/naruto-shadow-clone-swarm-check.js +88 -50
  41. package/dist/scripts/npm-publish-performance-check.js +2 -1
  42. package/dist/scripts/packlist-performance-check.js +5 -2
  43. package/dist/scripts/release-dag-full-coverage-check.js +7 -7
  44. package/dist/scripts/release-gate-existence-audit.js +8 -9
  45. package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
  46. package/dist/scripts/sizecheck.js +7 -2
  47. package/dist/scripts/sks-menubar-install-check.js +7 -0
  48. package/dist/scripts/update-default-command-check.js +4 -2
  49. package/package.json +3 -16
package/README.md CHANGED
@@ -25,7 +25,7 @@ sneakoscope is a proof-first Codex trust layer for bounded agent workflows, sear
25
25
 
26
26
  Sneakoscope Codex (`sks`) is a Codex CLI and Codex App harness for people who want parallel AI coding without losing proof. It gives Codex a simple front door, a dynamic worker swarm, a local dashboard, TriWiki project memory, and release gates that separate machine evidence from LLM opinion.
27
27
 
28
- Current release: SKS **6.0.2**. This release lets Codex own the complete model catalog—including current and future GPT-5.6 options—without SKS allowlists or silent model replacement. It also bounds agent, loop, and release concurrency to keep Codex Desktop responsive, preserves explicit user model/provider choices during repair and updates, and adds a clean-build verification path for `npm publish --ignore-scripts`. See [CHANGELOG.md](CHANGELOG.md).
28
+ Current release: SKS **6.0.3**. Normal Codex sessions continue to inherit the complete Codex catalog without silent replacement. Naruto workers now use a scoped, live-catalog-verified GPT-5.6 policy: Terra `xhigh/max` for coding, Sol `max` for refactoring/planning/strategy/integration, and Luna `xhigh/max` for E2E/browser/Computer Use/GUI verification. This release also fixes menu-bar auth restarts, cross-version menu updates, status checkmarks, dashboard readiness, and stale update badges. See [CHANGELOG.md](CHANGELOG.md).
29
29
 
30
30
  ## Install
31
31
 
@@ -107,6 +107,8 @@ It shows the v5 flow: one-line install, `$Plan`, `$Work`/`$Swarm`, `sks review`,
107
107
  - macOS optional: menu bar integration and `/usr/bin/open`
108
108
  - The menubar icon shows and hides itself automatically as the Codex desktop app launches/quits; set `quit_with_codex: true` in `~/.codex/sks-menubar/config.json` to have the menubar fully quit with Codex instead of just hiding (default `false`).
109
109
  - Native input dialogs (API keys, codex-lb setup) pass secrets to `sks` via `--api-key-stdin` instead of a visible Terminal window or process arguments.
110
+ - Auth/provider changes wait for the ChatGPT/Codex app to exit and reopen by bundle id; a failed restart is reported as a failed menu action.
111
+ - Update installs always rebuild the companion with the newly installed SKS package, preventing a previous-version updater from restoring a stale menu binary.
110
112
  - The menubar dropdown's `View Last Log` item opens the most recent background action's log file, so you don't need to keep a Terminal window open to see command output.
111
113
  - `sks menubar status --json` reports a `codex_sync` object with `bundle_id`, `codex_running`, and `icon_visible_expected` to show Codex-lifecycle detection state.
112
114
  - Zellij optional but recommended for terminal worker panes
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "6.0.2"
79
+ version = "6.0.3"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "6.0.2"
3
+ version = "6.0.3"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -151,3 +151,8 @@ exports.rootJsonFastInline = rootJsonFastInline;
151
151
  exports.doctorJsonFastInline = doctorJsonFastInline;
152
152
  exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
153
153
  exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
154
+
155
+ exports.rootJsonFastInline = rootJsonFastInline;
156
+ exports.doctorJsonFastInline = doctorJsonFastInline;
157
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
158
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
@@ -60,3 +60,5 @@ async function runSks(args) {
60
60
  }
61
61
 
62
62
  exports.runSks = runSks;
63
+
64
+ exports.runSks = runSks;
@@ -1397,6 +1397,30 @@ export async function releaseCodexLbAuthHold(opts = {}) {
1397
1397
  const authPath = opts.authPath || codexAuthPath(home);
1398
1398
  const backupPath = opts.backupPath || codexAuthChatgptBackupPath(home);
1399
1399
  const configPath = opts.configPath || codexLbConfigPath(home);
1400
+ const currentAuthText = await readText(authPath, '');
1401
+ const trimmedCurrent = currentAuthText.trim();
1402
+ // Repeated "Use ChatGPT OAuth" is idempotent. If OAuth/browser auth is
1403
+ // already active, a historical backup is unnecessary; only ensure that the
1404
+ // codex-lb provider is no longer selected.
1405
+ const currentAuthMode = codexAuthModeSummary(currentAuthText);
1406
+ if (!opts.force && (currentAuthMode.mode === 'chatgpt_oauth' || currentAuthMode.mode === 'browser_marker')) {
1407
+ let providerUnselected = false;
1408
+ let providerError = null;
1409
+ if (!opts.keepProvider) {
1410
+ const unselected = await unselectCodexLbProvider({ ...opts, home, configPath });
1411
+ if (unselected.status === 'unselected' || unselected.status === 'not_selected')
1412
+ providerUnselected = true;
1413
+ else if (unselected.status === 'failed')
1414
+ providerError = unselected.error || unselected.reason || 'unselect_failed';
1415
+ }
1416
+ return {
1417
+ status: providerError ? 'failed' : 'already_chatgpt',
1418
+ auth_path: authPath,
1419
+ backup_path: backupPath,
1420
+ provider_unselected: providerUnselected,
1421
+ provider_error: providerError
1422
+ };
1423
+ }
1400
1424
  const backupExists = await exists(backupPath);
1401
1425
  const backupText = backupExists ? await readText(backupPath, '') : '';
1402
1426
  if (!backupExists || !backupText.trim()) {
@@ -1416,8 +1440,6 @@ export async function releaseCodexLbAuthHold(opts = {}) {
1416
1440
  provider_unselected: false
1417
1441
  };
1418
1442
  }
1419
- const currentAuthText = await readText(authPath, '');
1420
- const trimmedCurrent = currentAuthText.trim();
1421
1443
  // If auth.json already looks like ChatGPT OAuth (user re-logged in some other way), don't
1422
1444
  // clobber it — but still honor the deselect request so the OAuth blob takes effect.
1423
1445
  if (trimmedCurrent && hasChatgptOAuthTokens(currentAuthText) && !opts.force) {
@@ -1910,7 +1932,9 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
1910
1932
  'browser_use', 'browser_use_external', 'image_generation', 'in_app_browser',
1911
1933
  'guardian_approval', 'tool_suggest', 'plugins'
1912
1934
  ]) {
1913
- next = removeTomlTableKey(next, 'features', legacyFlag);
1935
+ // Earlier SKS releases wrote these flags as `true`. Remove only that
1936
+ // managed legacy value; an explicit user `false` remains authoritative.
1937
+ next = removeTomlTableKey(next, 'features', legacyFlag, 'true');
1914
1938
  }
1915
1939
  if (opts.forceFastMode === true) {
1916
1940
  next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
@@ -1990,7 +2014,7 @@ function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
1990
2014
  const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?$`);
1991
2015
  return lines.filter((line, index) => index >= end || !keyPattern.test(line)).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
1992
2016
  }
1993
- function removeTomlTableKey(text, table, key) {
2017
+ function removeTomlTableKey(text, table, key, expectedValue = null) {
1994
2018
  const lines = String(text || '').trimEnd().split('\n');
1995
2019
  if (lines.length === 1 && lines[0] === '')
1996
2020
  return '';
@@ -2008,7 +2032,10 @@ function removeTomlTableKey(text, table, key) {
2008
2032
  break;
2009
2033
  }
2010
2034
  }
2011
- const keyPattern = new RegExp(`^\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=`);
2035
+ const valuePattern = expectedValue === null
2036
+ ? ''
2037
+ : `\\s*${escapeRegExp(String(expectedValue))}\\s*(?:#.*)?$`;
2038
+ const keyPattern = new RegExp(`^\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=${valuePattern}`);
2012
2039
  return lines.filter((line, index) => index <= start || index >= end || !keyPattern.test(line)).join('\n').replace(/\n{3,}/g, '\n\n');
2013
2040
  }
2014
2041
  function upsertTomlTableKey(text, table, line) {
@@ -2162,7 +2189,15 @@ export function codexFastModeDesktopStatus(text = '') {
2162
2189
  service_tier: validation.service_tier ?? null,
2163
2190
  model: validation.model ?? null,
2164
2191
  legacy_keys: validation.legacy_keys,
2165
- validation
2192
+ validation: {
2193
+ ok: validation.ok,
2194
+ blockers: validation.blockers,
2195
+ parse_error: validation.parse_error || null,
2196
+ service_tier: validation.service_tier ?? null,
2197
+ model: validation.model ?? null,
2198
+ model_reasoning_effort: validation.model_reasoning_effort ?? null,
2199
+ legacy_keys: validation.legacy_keys
2200
+ }
2166
2201
  };
2167
2202
  }
2168
2203
  function upsertTopLevelTomlBoolean(text, key, value) {
@@ -12,8 +12,11 @@ import { resolveCodexAppExecutionProfile } from '../core/codex-app/codex-app-exe
12
12
  import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
13
13
  import { doctorCodexAppGlmProfile, installCodexAppGlmProfile } from '../core/codex-app/glm-profile-installer.js';
14
14
  import { promptForOpenRouterKeyHidden, writeStoredOpenRouterKey } from '../core/providers/openrouter/openrouter-secret-store.js';
15
+ import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
15
16
  export async function run(_command, args = []) {
16
17
  const action = args[0] || 'check';
18
+ if (action === 'restart')
19
+ return printCodexAppResult(args, await restartCodexApp());
17
20
  if (action === 'remote-control' || action === 'remote')
18
21
  return codexAppRemoteControlCommand(args.slice(1));
19
22
  if (action === 'harness-matrix') {
@@ -47,15 +50,17 @@ export async function run(_command, args = []) {
47
50
  }
48
51
  const record = await writeStoredOpenRouterKey(key);
49
52
  const profile = await installCodexAppGlmProfile({ root, apply: true });
53
+ const restart = await restartCodexApp({ enabled: flag(args, '--restart-app') || flag(args, '--restart') });
50
54
  const result = {
51
55
  schema: 'sks.codex-app-openrouter-key.v1',
52
- ok: profile.ok,
53
- status: profile.ok ? 'stored' : 'stored_profile_blocked',
56
+ ok: Boolean(profile.ok && restart.ok),
57
+ status: !profile.ok ? 'stored_profile_blocked' : restart.ok ? 'stored' : 'stored_restart_blocked',
54
58
  key_preview: record.key_preview,
55
59
  raw_key_recorded: false,
56
60
  secret_store: 'sks-openrouter-secret-store',
57
61
  glm_profile: profile,
58
- blockers: profile.blockers || [],
62
+ restart_app: restart,
63
+ blockers: [...(profile.blockers || []), ...(restart.blockers || [])],
59
64
  warnings: profile.warnings || []
60
65
  };
61
66
  return printCodexAppResult(args, result);
@@ -126,8 +126,10 @@ export async function run(command, args = []) {
126
126
  const host = status.base_url;
127
127
  if (!host) {
128
128
  const result = { schema: 'sks.codex-lb-set-key.v1', ok: false, status: 'not_configured' };
129
- if (flag(args, '--json'))
129
+ if (flag(args, '--json')) {
130
+ process.exitCode = 1;
130
131
  return printJson(result);
132
+ }
131
133
  console.error('codex-lb is not configured yet. Run: sks codex-lb setup --host <domain> --api-key-stdin');
132
134
  process.exitCode = 1;
133
135
  return;
@@ -135,21 +137,26 @@ export async function run(command, args = []) {
135
137
  const newKey = await resolveNewApiKey(args);
136
138
  if (!newKey) {
137
139
  const result = { schema: 'sks.codex-lb-set-key.v1', ok: false, status: 'missing_api_key' };
138
- if (flag(args, '--json'))
140
+ if (flag(args, '--json')) {
141
+ process.exitCode = 1;
139
142
  return printJson(result);
143
+ }
140
144
  console.error('No new API key provided. Run: sks codex-lb set-key --api-key-stdin (or --api-key <key>)');
141
145
  process.exitCode = 1;
142
146
  return;
143
147
  }
144
148
  const result = await configureCodexLb({ host, apiKey: newKey, authMode: flag(args, '--preserve-auth') ? 'preserve' : 'codex-lb', forceCodexLbApiKeyAuth: !flag(args, '--preserve-auth') });
145
149
  const restart = await maybeRestartCodexAppForAuthSwitch(args, Boolean(result.ok));
146
- const output = { ...result, action: 'set-key', restart_app: restart };
150
+ const ok = Boolean(result.ok && restart?.ok !== false);
151
+ const output = { ...result, ok, action: 'set-key', restart_app: restart };
152
+ if (!ok)
153
+ process.exitCode = 1;
147
154
  if (flag(args, '--json'))
148
155
  return printJson(output);
149
156
  console.log(result.ok ? `codex-lb API key updated (${result.base_url || host}).` : `codex-lb key update failed: ${result.status}${result.error ? `: ${result.error}` : ''}`);
150
157
  if (restart?.status === 'restarted')
151
158
  console.log('Codex App restarted for the new codex-lb auth mode.');
152
- if (!result.ok)
159
+ if (!ok)
153
160
  process.exitCode = 1;
154
161
  return;
155
162
  }
@@ -157,21 +164,30 @@ export async function run(command, args = []) {
157
164
  // Switch auth mode -> codex-lb (API key). Re-selects the provider and re-syncs auth.
158
165
  const result = await repairCodexLbAuth({ forceCodexLbApiKeyAuth: true, authMode: 'codex-lb', forceFastMode: !flag(args, '--no-fast') });
159
166
  const restart = await maybeRestartCodexAppForAuthSwitch(args, Boolean(result.ok));
167
+ const ok = Boolean(result.ok && restart?.ok !== false);
168
+ const shaped = { ...result, ok, mode: 'codex-lb', restart_app: restart };
169
+ if (!ok)
170
+ process.exitCode = 1;
160
171
  if (flag(args, '--json'))
161
- return printJson({ ...result, mode: 'codex-lb', restart_app: restart });
172
+ return printJson(shaped);
162
173
  console.log(result.ok ? 'Auth mode: codex-lb selected (API key).' : `Switch to codex-lb failed: ${result.status}${result.error ? `: ${result.error}` : ''}`);
163
174
  if (restart?.status === 'restarted')
164
175
  console.log('Codex App restarted for codex-lb auth.');
165
- if (!result.ok)
176
+ if (!ok)
166
177
  process.exitCode = 1;
167
178
  return;
168
179
  }
169
180
  if (action === 'use-oauth' || action === 'use-chatgpt') {
170
181
  // Switch auth mode -> ChatGPT OAuth. Restores the saved OAuth login if present.
171
182
  const result = await releaseCodexLbAuthHold({ force: flag(args, '--force') });
172
- const restart = await maybeRestartCodexAppForAuthSwitch(args, !['no_backup', 'auth_in_use', 'failed'].includes(result.status));
183
+ const authOk = !['no_backup', 'auth_in_use', 'failed'].includes(result.status);
184
+ const restart = await maybeRestartCodexAppForAuthSwitch(args, authOk);
185
+ const ok = Boolean(authOk && restart?.ok !== false);
186
+ const shaped = { ...result, ok, mode: 'oauth', restart_app: restart };
187
+ if (!ok)
188
+ process.exitCode = 1;
173
189
  if (flag(args, '--json'))
174
- return printJson({ ...result, mode: 'oauth', restart_app: restart });
190
+ return printJson(shaped);
175
191
  if (result.status === 'no_backup') {
176
192
  console.log('No saved ChatGPT OAuth credentials to restore. Switch to OAuth by logging in:');
177
193
  console.log(' codex login');
@@ -210,8 +226,10 @@ export async function run(command, args = []) {
210
226
  'Or: sks codex-lb setup --host <domain> --api-key-stdin --yes'
211
227
  ]
212
228
  };
213
- if (flag(args, '--json'))
229
+ if (flag(args, '--json')) {
230
+ process.exitCode = 1;
214
231
  return printJson(result);
232
+ }
215
233
  console.error('codex-lb API key is not configured.');
216
234
  console.error('Run:');
217
235
  console.error(' sks codex-lb setup');
@@ -222,6 +240,8 @@ export async function run(command, args = []) {
222
240
  }
223
241
  if (flag(args, '--plan')) {
224
242
  const result = { schema: 'sks.codex-lb-setup-plan-result.v1', ok: plan.blockers.length === 0, plan, writes: false, expected_actions: plan.expected_actions, persistence: plan.persistence };
243
+ if (!result.ok)
244
+ process.exitCode = 1;
225
245
  if (flag(args, '--json'))
226
246
  return printJson(result);
227
247
  process.stdout.write(renderCodexLbSetupPlan(plan));
@@ -235,8 +255,10 @@ export async function run(command, args = []) {
235
255
  const confirm = (await ask('Apply this codex-lb setup plan? [y/N] ')).trim();
236
256
  if (!/^(y|yes|예|네|응)$/i.test(confirm)) {
237
257
  const result = { schema: 'sks.codex-lb-setup.v1', ok: false, status: 'cancelled', plan, applied_actions: [] };
238
- if (flag(args, '--json'))
258
+ if (flag(args, '--json')) {
259
+ process.exitCode = 1;
239
260
  return printJson(result);
261
+ }
240
262
  console.log('codex-lb setup cancelled.');
241
263
  process.exitCode = 1;
242
264
  return;
@@ -298,12 +320,15 @@ export async function run(command, args = []) {
298
320
  shaped.applied_actions = [...(shaped.applied_actions || []), { type: 'run_health_check', target: 'codex-lb response chain', ok: true }];
299
321
  if (options.health)
300
322
  shaped.chain_health = result.ok ? await checkCodexLbResponseChain(result, { force: true, root }) : null;
323
+ shaped.ok = Boolean(result.ok && restart?.ok !== false);
324
+ if (!shaped.ok)
325
+ process.exitCode = 1;
301
326
  if (flag(args, '--json'))
302
327
  return printJson(shaped);
303
328
  console.log(`codex-lb configured: ${result.base_url || result.status}`);
304
329
  if (shaped.persistence?.warning)
305
330
  console.log(`warning: ${shaped.persistence.warning}`);
306
- if (!result.ok)
331
+ if (!shaped.ok)
307
332
  process.exitCode = 1;
308
333
  return;
309
334
  }
@@ -345,7 +370,7 @@ export async function run(command, args = []) {
345
370
  }
346
371
  async function maybeRestartCodexAppForAuthSwitch(args = [], enabled) {
347
372
  if (!enabled)
348
- return { schema: 'sks.codex-app-restart.v1', ok: true, status: 'skipped', skipped: true, reason: 'previous_step_failed', app_name: 'Codex', blockers: [] };
373
+ return { schema: 'sks.codex-app-restart.v1', ok: true, status: 'skipped', skipped: true, reason: 'previous_step_failed', app_name: 'ChatGPT', bundle_id: 'com.openai.codex', blockers: [] };
349
374
  const requested = flag(args, '--restart-app') || flag(args, '--restart');
350
375
  const shouldRestart = requested || (!flag(args, '--json') && !flag(args, '--no-restart-app') && !flag(args, '--no-restart'));
351
376
  return restartCodexApp({ enabled: shouldRestart });