sneakoscope 8.2.1 → 8.3.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.
Files changed (128) hide show
  1. package/README.md +50 -112
  2. package/config/skills-hash-ledger.v1.json +3 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/cli/command-manifest-lite.js +0 -2
  6. package/dist/cli/command-registry.js +0 -2
  7. package/dist/commands/codex.js +3 -3
  8. package/dist/commands/doctor.js +5 -157
  9. package/dist/config/skills-manifest.json +7 -4
  10. package/dist/core/codex/codex-cli-syntax-builder.js +0 -8
  11. package/dist/core/codex-app/codex-models-cache.js +2 -1
  12. package/dist/core/codex-app/menubar/config.js +4 -4
  13. package/dist/core/codex-app/menubar/constants.js +2 -9
  14. package/dist/core/codex-app/menubar/global-install.js +16 -0
  15. package/dist/core/codex-app/menubar/installer.js +15 -2
  16. package/dist/core/codex-app/menubar/launch-agent.js +23 -9
  17. package/dist/core/codex-app/menubar/rollback.js +12 -2
  18. package/dist/core/codex-compat/codex-capability-matrix.js +7 -7
  19. package/dist/core/codex-compat/codex-compat-report.js +8 -12
  20. package/dist/core/codex-compat/codex-runtime-contract.js +62 -0
  21. package/dist/core/codex-compat/codex-version-policy.js +7 -7
  22. package/dist/core/codex-control/codex-current-app-capability.js +3 -3
  23. package/dist/core/codex-control/codex-current-capability.js +67 -94
  24. package/dist/core/codex-control/codex-current-core-capability.js +3 -3
  25. package/dist/core/codex-control/codex-current-core-probe-runner.js +4 -4
  26. package/dist/core/codex-control/codex-current-core-real-probes.js +2 -2
  27. package/dist/core/codex-control/codex-current-feature-capability.js +4 -4
  28. package/dist/core/codex-control/codex-current-image-path-real-probe.js +1 -1
  29. package/dist/core/codex-control/codex-current-web-search-probe.js +1 -1
  30. package/dist/core/codex-control/codex-sdk-capability.js +2 -2
  31. package/dist/core/codex-lb/combined-catalog/normalize.js +36 -3
  32. package/dist/core/codex-lb/desktop-bridge/header-policy.js +5 -3
  33. package/dist/core/codex-lb/desktop-bridge/http-forward.js +48 -3
  34. package/dist/core/codex-lb/desktop-bridge/server.js +13 -7
  35. package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +2 -1
  36. package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +46 -8
  37. package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +12 -2
  38. package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +34 -8
  39. package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +13 -4
  40. package/dist/core/codex-lb/desktop-controller-v3/shared.js +2 -1
  41. package/dist/core/codex-lb/desktop-controller-v3/verification.js +10 -9
  42. package/dist/core/codex-lb/desktop-service.js +57 -4
  43. package/dist/core/codex-lb/route-index.js +1 -0
  44. package/dist/core/commands/remote-command.js +2 -2
  45. package/dist/core/context7-client.js +5 -2
  46. package/dist/core/doctor/codex-current-core-probe-doctor.js +2 -2
  47. package/dist/core/doctor/doctor-dirty-planner.js +1 -1
  48. package/dist/core/doctor/doctor-readiness-matrix.js +0 -7
  49. package/dist/core/feature-fixtures.js +0 -4
  50. package/dist/core/feature-registry.js +0 -1
  51. package/dist/core/image-ux-review.js +3 -3
  52. package/dist/core/install/installed-package-smoke.js +2 -3
  53. package/dist/core/mad-sks/sql-plane/mcp-executor.js +4 -3
  54. package/dist/core/managed-assets/managed-assets-manifest.js +1 -0
  55. package/dist/core/mcp/modern-protocol.js +36 -0
  56. package/dist/core/mcp-config/health-check.js +45 -30
  57. package/dist/core/perf/release-latency-slo.js +1 -2
  58. package/dist/core/proof/runtime-truth-matrix.js +1 -1
  59. package/dist/core/release/canonical-test-proof.js +9 -3
  60. package/dist/core/release/gate-manifest.js +1 -2
  61. package/dist/core/release/physical-release-gates.js +0 -26
  62. package/dist/core/release/release-gate-affected-selector.js +0 -1
  63. package/dist/core/release/release-gate-contract.js +1 -1
  64. package/dist/core/release/release-pack-receipt.js +0 -1
  65. package/dist/core/release/stage-publish.js +1 -1
  66. package/dist/core/routes/evidence.js +1 -1
  67. package/dist/core/version.js +1 -1
  68. package/dist/native/sks-menubar/Sources/AppDelegate.swift +1 -12
  69. package/dist/native/sks-menubar/Sources/CodexLifecyclePolicy.swift +18 -0
  70. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +2 -6
  71. package/dist/native/sks-menubar/Sources/ProcessClient.swift +3 -1
  72. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +86 -2
  73. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +49 -15
  74. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +28 -470
  75. package/dist/native/sks-menubar/Sources/SKSKeychainStore.swift +21 -1
  76. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +1 -43
  77. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +18 -17
  78. package/dist/native/sks-menubar/Sources/StatusItemController.swift +14 -14
  79. package/dist/scripts/canonical-test-runner.js +83 -3
  80. package/dist/scripts/codex-current-app-server-v2-check.js +9 -8
  81. package/dist/scripts/codex-current-binary-identity-check.js +2 -2
  82. package/dist/scripts/codex-current-capability-check.js +9 -7
  83. package/dist/scripts/codex-current-dependency-check.js +21 -0
  84. package/dist/scripts/docs-truthfulness-check.js +4 -19
  85. package/dist/scripts/fast-codex-service-tier-proof-check.js +0 -1
  86. package/dist/scripts/latest-version-guidance-check.js +1 -1
  87. package/dist/scripts/packlist-performance-check.js +0 -1
  88. package/dist/scripts/release-metadata-check.js +1 -1
  89. package/dist/vendor/openai-codex/latest/hooks/permission-request.command.input.schema.json +6 -0
  90. package/dist/vendor/openai-codex/latest/hooks/permission-request.command.output.schema.json +2 -16
  91. package/dist/vendor/openai-codex/latest/hooks/post-compact.command.input.schema.json +6 -0
  92. package/dist/vendor/openai-codex/latest/hooks/post-tool-use.command.input.schema.json +6 -0
  93. package/dist/vendor/openai-codex/latest/hooks/post-tool-use.command.output.schema.json +2 -16
  94. package/dist/vendor/openai-codex/latest/hooks/pre-compact.command.input.schema.json +6 -0
  95. package/dist/vendor/openai-codex/latest/hooks/pre-tool-use.command.input.schema.json +6 -0
  96. package/dist/vendor/openai-codex/latest/hooks/pre-tool-use.command.output.schema.json +2 -16
  97. package/dist/vendor/openai-codex/latest/hooks/session-start.command.output.schema.json +2 -16
  98. package/dist/vendor/openai-codex/latest/hooks/snapshot-metadata.json +7 -5
  99. package/dist/vendor/openai-codex/latest/hooks/subagent-start.command.output.schema.json +4 -18
  100. package/dist/vendor/openai-codex/latest/hooks/user-prompt-submit.command.input.schema.json +6 -0
  101. package/dist/vendor/openai-codex/latest/hooks/user-prompt-submit.command.output.schema.json +2 -16
  102. package/package.json +3 -7
  103. package/release-gates.v2.json +10 -17
  104. package/config/codex-releases/rust-v0.146.0.json +0 -45
  105. package/dist/core/agents/agent-runner-codex-exec.js +0 -177
  106. package/dist/core/codex-compat/codex-release-manifest.js +0 -72
  107. package/dist/core/commands/telegram-command.js +0 -447
  108. package/dist/core/telegram/access.js +0 -58
  109. package/dist/core/telegram/client.js +0 -157
  110. package/dist/core/telegram/confirmation.js +0 -37
  111. package/dist/core/telegram/doctor.js +0 -159
  112. package/dist/core/telegram/index.js +0 -9
  113. package/dist/core/telegram/keychain.js +0 -317
  114. package/dist/core/telegram/liveness.js +0 -124
  115. package/dist/core/telegram/poller.js +0 -90
  116. package/dist/core/telegram/redaction.js +0 -15
  117. package/dist/core/telegram/state-lock.js +0 -416
  118. package/dist/core/telegram/types.js +0 -1
  119. package/dist/native/sks-menubar/Sources/RemoteCodingSettingsControls.swift +0 -160
  120. package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +0 -498
  121. package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +0 -236
  122. package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +0 -454
  123. package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +0 -56
  124. package/dist/native/sks-menubar/Sources/TelegramStateLock.swift +0 -448
  125. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +0 -244
  126. package/dist/native/sks-menubar/Sources/TelegramTransport.swift +0 -499
  127. package/dist/scripts/codex-current-manifest-check.js +0 -29
  128. package/schemas/codex/app-server-0.146/codex_app_server_protocol.v2.schemas.json +0 -19858
@@ -1,13 +1,7 @@
1
1
  export function buildCodexExecArgs(opts) {
2
- if (opts.fullAuto && opts.danger) {
3
- throw new Error('codex exec cannot combine full auto and danger modes');
4
- }
5
2
  if (opts.danger && !opts.allowDanger) {
6
3
  throw new Error('codex exec danger mode requires explicit allowDanger=true');
7
4
  }
8
- if (opts.fullAuto && !opts.allowFullAuto) {
9
- throw new Error('codex exec full-auto mode requires explicit allowFullAuto=true');
10
- }
11
5
  if (opts.profile && opts.ignoreUserConfig) {
12
6
  throw new Error('codex exec cannot combine --profile with --ignore-user-config');
13
7
  }
@@ -30,8 +24,6 @@ export function buildCodexExecArgs(opts) {
30
24
  args.push('--ignore-user-config');
31
25
  if (opts.ignoreRules)
32
26
  args.push('--ignore-rules');
33
- if (opts.fullAuto)
34
- args.push('--full-auto');
35
27
  if (opts.danger)
36
28
  args.push('--dangerously-bypass-approvals-and-sandbox');
37
29
  else if (opts.sandbox)
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { writeTextAtomic } from '../fsx.js';
4
4
  import { codexHomePath } from './codex-model-catalog.js';
5
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from '../codex-compat/codex-runtime-contract.js';
5
6
  export const CODEX_MODELS_CACHE_STALE_FETCHED_AT = '2000-01-01T00:00:00Z';
6
7
  export const CODEX_MODELS_CACHE_FILENAME = 'models_cache.json';
7
8
  export function codexModelsCachePath(input = {}) {
@@ -116,7 +117,7 @@ export async function invalidateCodexModelsCache(input = {}) {
116
117
  const existingClient = typeof existing?.client_version === 'string' ? existing.client_version.trim() : '';
117
118
  const clientVersion = existingClient && existingClient !== '0.0.0'
118
119
  ? existingClient
119
- : '0.146.0';
120
+ : CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion;
120
121
  const wrapper = {
121
122
  fetched_at: CODEX_MODELS_CACHE_STALE_FETCHED_AT,
122
123
  client_version: clientVersion,
@@ -4,18 +4,18 @@ import { readJson, runProcess, which, writeJsonAtomic } from '../../fsx.js';
4
4
  export async function readMenuBarConfig(configPath) {
5
5
  const value = await readJson(configPath, null);
6
6
  return {
7
- schema: 'sks.sks-menubar-config.v1',
7
+ schema: 'sks.sks-menubar-config.v2',
8
8
  codex_bundle_id: typeof value?.codex_bundle_id === 'string' && value.codex_bundle_id.trim()
9
9
  ? value.codex_bundle_id.trim() : null,
10
- quit_with_codex: value?.quit_with_codex === true
10
+ follow_codex_lifecycle: value?.follow_codex_lifecycle === true || value?.quit_with_codex === true
11
11
  };
12
12
  }
13
13
  export async function writeDefaultMenuBarConfig(configPath, codexBundleId) {
14
14
  const previous = await readMenuBarConfig(configPath);
15
15
  const value = {
16
- schema: 'sks.sks-menubar-config.v1',
16
+ schema: 'sks.sks-menubar-config.v2',
17
17
  codex_bundle_id: codexBundleId || previous.codex_bundle_id,
18
- quit_with_codex: previous.quit_with_codex
18
+ follow_codex_lifecycle: previous.follow_codex_lifecycle
19
19
  };
20
20
  await writeJsonAtomic(configPath, value);
21
21
  return value;
@@ -6,12 +6,13 @@ export const MENU_ITEMS = [
6
6
  'Pending approvals',
7
7
  'Check for Updates',
8
8
  'View Last Operation',
9
- 'Quit SKS Menu'
9
+ 'Hide Until Codex Opens'
10
10
  ];
11
11
  export const NATIVE_SOURCE_FILES = [
12
12
  'main.swift',
13
13
  'AppDelegate.swift',
14
14
  'StatusItemController.swift',
15
+ 'CodexLifecyclePolicy.swift',
15
16
  'ControlCenterWindowController.swift',
16
17
  'SidebarItem.swift',
17
18
  'ControlKit.swift',
@@ -25,7 +26,6 @@ export const NATIVE_SOURCE_FILES = [
25
26
  'ProvidersOpenRouter.swift',
26
27
  'ProvidersBridgeCatalog.swift',
27
28
  'RemoteCodingViewController.swift',
28
- 'RemoteCodingSettingsControls.swift',
29
29
  'DiagnosticsViewController.swift',
30
30
  'SettingsViewController.swift',
31
31
  'OperationModels.swift',
@@ -36,13 +36,6 @@ export const NATIVE_SOURCE_FILES = [
36
36
  'ProcessIdentityGuard.swift',
37
37
  'SecureProcessEnvelope.swift',
38
38
  'SKSKeychainStore.swift',
39
- 'TelegramStateLock.swift',
40
- 'TelegramPrivateFileSupport.swift',
41
- 'TelegramPrivateFileStore.swift',
42
- 'TelegramSupport.swift',
43
- 'TelegramRuntimeSupport.swift',
44
- 'TelegramTransport.swift',
45
- 'TelegramProcessGateway.swift',
46
39
  'NotificationCoordinator.swift',
47
40
  'AlertFactory.swift',
48
41
  'AppIdentity.swift',
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { exists, nowIso, readJson, readText, runProcess, writeJsonAtomic } from '../../fsx.js';
3
3
  import { ensureConfinedDirectory, inspectConfinedPath, removeManagedPathVerified } from '../../managed-path-safety.js';
4
+ import { realUserHome } from './installer/runtime.js';
4
5
  const MENU_BAR_EXECUTABLE_SUFFIX = path.join('SKSMenuBar.app', 'Contents', 'MacOS', 'SKSMenuBar');
5
6
  const MENU_BAR_PROCESS_DISCOVERY_TIMEOUT_MS = 5_000;
6
7
  export function projectMenuBarDuplicateInstallDirs(input) {
@@ -21,6 +22,8 @@ export async function verifiedProjectMenuBarDuplicateExecutablePaths(input) {
21
22
  const executables = [];
22
23
  const candidates = await discoverProjectMenuBarDuplicateCandidates(input);
23
24
  for (const candidate of candidates.inspected) {
25
+ if (isOutsideScopeReapingRefused(candidate, root, home))
26
+ continue;
24
27
  const confinementRoot = confinementRootForCandidate(root, home, candidate);
25
28
  const state = await inspectConfinedPath(confinementRoot, candidate).catch(() => null);
26
29
  if (!state?.exists || state.leafSymlink || !state.stat?.isDirectory())
@@ -96,6 +99,11 @@ export async function cleanupProjectMenuBarDuplicates(input) {
96
99
  const blockers = [];
97
100
  const warnings = [];
98
101
  for (const candidate of inspected) {
102
+ if (isOutsideScopeReapingRefused(candidate, root, input.paths.home)) {
103
+ preserved.push(candidate);
104
+ warnings.push(`menubar_project_duplicate_outside_scope_preserved:${candidate}`);
105
+ continue;
106
+ }
99
107
  const confinementRoot = confinementRootForCandidate(root, input.paths.home, candidate);
100
108
  const state = await inspectConfinedPath(confinementRoot, candidate).catch(() => null);
101
109
  if (!state) {
@@ -254,6 +262,14 @@ function duplicateInstallDirForExecutable(executablePath, canonicalInstallDir) {
254
262
  return null;
255
263
  return executable.slice(0, -suffix.length);
256
264
  }
265
+ function isOutsideScopeReapingRefused(candidateInput, rootInput, homeInput) {
266
+ const candidate = path.resolve(candidateInput);
267
+ const root = path.resolve(rootInput);
268
+ const home = path.resolve(homeInput);
269
+ if (isWithin(root, candidate) || isWithin(home, candidate))
270
+ return false;
271
+ return home !== realUserHome();
272
+ }
257
273
  function confinementRootForCandidate(rootInput, homeInput, candidate) {
258
274
  const root = path.resolve(rootInput);
259
275
  const home = path.resolve(homeInput);
@@ -6,7 +6,7 @@ import { aggregateFileHashes, createSksMenuBarBuildStamp, menuBarBuildStampsEqua
6
6
  import { SKS_MENUBAR_LABEL } from './constants.js';
7
7
  import { resolveCodexBundleId, writeDefaultMenuBarConfig } from './config.js';
8
8
  import { applyMenuBarGenerationTransaction, commitMenuBarGenerationTransaction, installGenerationPairs, MenuBarGenerationTransactionError, recoverMenuBarGenerationTransaction, rollbackGenerationPairs } from './generation-transaction.js';
9
- import { launchAgentSource, launchMenuBar, inspectRunningMenuBarProcess, seedMenuBarPreferredPosition, stopMenuBarForReplacement, terminateMenuBarProcesses } from './launch-agent.js';
9
+ import { launchAgentSource, launchMenuBar, inspectRunningMenuBarProcess, menuBarLaunchdTouchAllowed, seedMenuBarPreferredPosition, stopMenuBarForReplacement, terminateMenuBarProcesses } from './launch-agent.js';
10
10
  import { cleanupProjectMenuBarDuplicates, verifiedProjectMenuBarDuplicateExecutablePaths } from './global-install.js';
11
11
  import { cleanupRetiredRemoteBridgeLaunchAgent, quarantineRetiredRemoteBridgeBindings } from './migration.js';
12
12
  import { sksMenuBarPaths } from './paths.js';
@@ -114,6 +114,11 @@ export async function installSksMenuBar(opts = {}) {
114
114
  return blocked('sks_entry_unresolved');
115
115
  if (targetCheck.project_local)
116
116
  warnings.push('sks_entry_project_local');
117
+ const macosProtectedRoots = ['Desktop', 'Documents', 'Downloads']
118
+ .map((dir) => `${path.join(paths.home, dir)}${path.sep}`);
119
+ if (targetCheck.resolved && macosProtectedRoots.some((root) => targetCheck.resolved.startsWith(root))) {
120
+ warnings.push('sks_entry_macos_protected_folder');
121
+ }
117
122
  const previousActionScript = await readText(paths.action_script_path, '');
118
123
  const previousLaunchAgent = await readText(paths.launch_agent_path, '');
119
124
  const previousRaw = await readJson(paths.build_stamp_path, null);
@@ -256,7 +261,15 @@ export async function installSksMenuBar(opts = {}) {
256
261
  });
257
262
  const mustStopCanonical = !upToDate || (launchWanted && launchAllowed);
258
263
  let canonicalStopped = false;
259
- if (mustStopCanonical) {
264
+ if (mustStopCanonical && !menuBarLaunchdTouchAllowed(paths, env)) {
265
+ const stopped = await terminateMenuBarProcesses([paths.executable_path, ...duplicateExecutablePaths], env);
266
+ if (!stopped.ok) {
267
+ return blocked('menubar_stop_before_replace_failed', `Remaining PIDs: ${stopped.remaining_pids.join(',')}`);
268
+ }
269
+ warnings.push('launchd_stop_skipped_non_user_home');
270
+ actions.push('verified canonical/project duplicate processes stopped without touching the user launchd service');
271
+ }
272
+ else if (mustStopCanonical) {
260
273
  const stopped = await stopMenuBarForReplacement({
261
274
  launchctl,
262
275
  paths,
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { exists, readJson, runProcess, which, writeJsonAtomic } from '../../fsx.js';
4
4
  import { CONTROL_CENTER_DOMAIN, CONTROL_CENTER_PREFERRED_POSITION, SKS_MENUBAR_LABEL } from './constants.js';
5
+ import { realUserHome } from './installer/runtime.js';
5
6
  export function launchAgentSource(executablePath, installDir) {
6
7
  return `<?xml version="1.0" encoding="UTF-8"?>
7
8
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -9,11 +10,15 @@ export function launchAgentSource(executablePath, installDir) {
9
10
  <key>Label</key><string>${SKS_MENUBAR_LABEL}</string>
10
11
  <key>ProgramArguments</key><array><string>${xml(executablePath)}</string></array>
11
12
  <key>RunAtLoad</key><true/>
13
+ <key>KeepAlive</key><true/>
12
14
  <key>ProcessType</key><string>Interactive</string>
13
15
  <key>StandardOutPath</key><string>${xml(path.join(installDir, 'menubar.out.log'))}</string>
14
16
  <key>StandardErrorPath</key><string>${xml(path.join(installDir, 'menubar.err.log'))}</string>
15
17
  </dict></plist>\n`;
16
18
  }
19
+ export function menuBarLaunchdTouchAllowed(paths, env) {
20
+ return Boolean(env.SKS_MENUBAR_LAUNCHCTL) || path.resolve(paths.home) === realUserHome();
21
+ }
17
22
  export async function seedMenuBarPreferredPosition(env) {
18
23
  const defaults = env.SKS_MENUBAR_DEFAULTS || await which('defaults').catch(() => null) || '/usr/bin/defaults';
19
24
  const writes = [
@@ -304,15 +309,24 @@ export async function removeLaunchAgent(paths, env) {
304
309
  const actions = [];
305
310
  const warnings = [];
306
311
  const blockers = [];
307
- const launchctl = env.SKS_MENUBAR_LAUNCHCTL || await which('launchctl').catch(() => null) || '/bin/launchctl';
308
- const stopped = await stopMenuBarForReplacement({
309
- launchctl,
310
- paths,
311
- executablePaths: [paths.executable_path],
312
- env
313
- });
314
- if (!stopped.ok)
315
- blockers.push(stopped.error || 'menubar_process_stop_failed');
312
+ if (menuBarLaunchdTouchAllowed(paths, env)) {
313
+ const launchctl = env.SKS_MENUBAR_LAUNCHCTL || await which('launchctl').catch(() => null) || '/bin/launchctl';
314
+ const stopped = await stopMenuBarForReplacement({
315
+ launchctl,
316
+ paths,
317
+ executablePaths: [paths.executable_path],
318
+ env
319
+ });
320
+ if (!stopped.ok)
321
+ blockers.push(stopped.error || 'menubar_process_stop_failed');
322
+ }
323
+ else {
324
+ const terminated = await terminateMenuBarProcesses([paths.executable_path], env);
325
+ if (!terminated.ok)
326
+ blockers.push('menubar_process_stop_failed');
327
+ else
328
+ warnings.push('launchd_stop_skipped_non_user_home');
329
+ }
316
330
  if (blockers.length > 0)
317
331
  return { actions, warnings, blockers };
318
332
  await fs.rm(paths.launch_agent_path, { force: true }).catch((error) => blockers.push(`remove_launch_agent_failed:${String(error)}`));
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { readJson, sha256, which } from '../../fsx.js';
4
4
  import { applyMenuBarGenerationTransaction, commitMenuBarGenerationTransaction, installGenerationPairs, MenuBarGenerationTransactionError, recoverMenuBarGenerationTransaction, rollbackGenerationPairs } from './generation-transaction.js';
5
- import { launchMenuBar, stopMenuBarForReplacement } from './launch-agent.js';
5
+ import { launchMenuBar, menuBarLaunchdTouchAllowed, stopMenuBarForReplacement, terminateMenuBarProcesses } from './launch-agent.js';
6
6
  import { sksMenuBarPaths } from './paths.js';
7
7
  import { aggregateFileHashes } from './build-stamp.js';
8
8
  import { inspectInstalledResources } from './resources.js';
@@ -286,7 +286,7 @@ export async function rollbackSksMenuBar(opts = {}) {
286
286
  else {
287
287
  transaction = committed;
288
288
  }
289
- if (opts.launch === false || env.SKS_SKIP_SKS_MENUBAR_LAUNCH === '1') {
289
+ if (opts.launch === false || env.SKS_SKIP_SKS_MENUBAR_LAUNCH === '1' || !menuBarLaunchdTouchAllowed(paths, env)) {
290
290
  return result('rolled_back_launch_skipped', true, verificationBefore.package_version, replacedStamp?.package_version || null, verificationBefore, verificationAfter, { requested: false, method: 'skipped', ok: true }, [], warnings);
291
291
  }
292
292
  const launch = await launchMenuBar({
@@ -307,6 +307,16 @@ export async function rollbackSksMenuBar(opts = {}) {
307
307
  async function stopBeforeReplacement() {
308
308
  if (replacementStopped)
309
309
  return null;
310
+ if (!menuBarLaunchdTouchAllowed(paths, env)) {
311
+ const terminated = await terminateMenuBarProcesses([paths.executable_path], env);
312
+ if (!terminated.ok) {
313
+ return result('failed', false, null, null, null, null, { requested: false, method: 'none', ok: false, error: 'menubar_rollback_stop_failed' }, ['menubar_rollback_stop_failed'], warnings);
314
+ }
315
+ replacementStopped = true;
316
+ warnings.push('launchd_stop_skipped_non_user_home');
317
+ actions.push('verified process termination without touching the user launchd service before journaled rollback replacement');
318
+ return null;
319
+ }
310
320
  const stopped = await stopMenuBarForReplacement({
311
321
  launchctl,
312
322
  paths,
@@ -1,5 +1,5 @@
1
1
  import { runProcess } from '../fsx.js';
2
- import { CURRENT_CODEX_RELEASE_MANIFEST, NARUTO_REQUIRED_CODEX_VERSION } from './codex-release-manifest.js';
2
+ import { CURRENT_CODEX_RUNTIME_CONTRACT, NARUTO_REQUIRED_CODEX_VERSION } from './codex-runtime-contract.js';
3
3
  import { compareSemverLike, parseCodexVersionText } from './codex-version-policy.js';
4
4
  export const CODEX_CAPABILITY_MATRIX_SCHEMA = 'sks.codex-capability-matrix.v1';
5
5
  export const SKS_CODEX_CAPABILITY_IDS = [
@@ -13,10 +13,10 @@ export const SKS_CODEX_CAPABILITY_IDS = [
13
13
  ];
14
14
  const UPDATE_CTA = 'Update Codex CLI to the preferred latest: `sks codex update` or Menu Bar / SKS Center → Update Codex CLI Now.';
15
15
  export function preferredCodexCliVersion() {
16
- return CURRENT_CODEX_RELEASE_MANIFEST.preferredCliVersion || CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion;
16
+ return CURRENT_CODEX_RUNTIME_CONTRACT.preferredCliVersion || CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion;
17
17
  }
18
18
  export function softMinimumCodexCliVersion() {
19
- return CURRENT_CODEX_RELEASE_MANIFEST.minimumSupportedVersion;
19
+ return CURRENT_CODEX_RUNTIME_CONTRACT.minimumSupportedVersion;
20
20
  }
21
21
  export function narutoCodexFloorVersion() {
22
22
  return NARUTO_REQUIRED_CODEX_VERSION;
@@ -26,7 +26,7 @@ export function buildCodexCapabilityMatrix(input = {}) {
26
26
  const help = String(input.helpText || '');
27
27
  const config = String(input.configText || '');
28
28
  const schema = String(input.schemaText || '');
29
- const policies = CURRENT_CODEX_RELEASE_MANIFEST.featurePolicies;
29
+ const policies = CURRENT_CODEX_RUNTIME_CONTRACT.featurePolicies;
30
30
  const warnings = [];
31
31
  if (detected && compareSemverLike(detected, preferredCodexCliVersion()) < 0) {
32
32
  warnings.push(`detected Codex ${detected} is below preferred ${preferredCodexCliVersion()}; prefer latest via SKS update inducement`);
@@ -122,8 +122,8 @@ export async function probeNarutoCodexCapability(input) {
122
122
  return { matrix, naruto: assertNarutoMultiAgentV2Capability(matrix) };
123
123
  }
124
124
  function resolveMultiAgentV2(input) {
125
- const policy = CURRENT_CODEX_RELEASE_MANIFEST.featurePolicies.multiAgentV2
126
- || CURRENT_CODEX_RELEASE_MANIFEST.featurePolicies.multiAgentMode
125
+ const policy = CURRENT_CODEX_RUNTIME_CONTRACT.featurePolicies.multiAgentV2
126
+ || CURRENT_CODEX_RUNTIME_CONTRACT.featurePolicies.multiAgentMode
127
127
  || 'delegate';
128
128
  const evidence = [];
129
129
  const helpHit = /\bmulti_agent_v2\b/.test(input.help);
@@ -167,7 +167,7 @@ function resolveMultiAgentV2(input) {
167
167
  };
168
168
  }
169
169
  function resolveAgentsConcurrency(input) {
170
- const policy = CURRENT_CODEX_RELEASE_MANIFEST.featurePolicies.agentsMaxConcurrentThreads || 'delegate';
170
+ const policy = CURRENT_CODEX_RUNTIME_CONTRACT.featurePolicies.agentsMaxConcurrentThreads || 'delegate';
171
171
  const hit = /max_concurrent_threads_per_session/.test(input.help)
172
172
  || /max_concurrent_threads_per_session/.test(input.config);
173
173
  const available = hit || input.multiAgentV2;
@@ -2,21 +2,18 @@ import { projectRoot } from '../fsx.js';
2
2
  import { detectCodexCurrentCapability } from '../codex-control/codex-current-capability.js';
3
3
  import { codexVersionReport } from './codex-version.js';
4
4
  import { CODEX_COMPAT_SCHEMA, CODEX_HOOK_SCHEMA_BASELINE_TAG, CODEX_REQUIRED_BASELINE_TAG } from './codex-version-policy.js';
5
- import { codexReleaseManifestParity } from './codex-release-manifest.js';
5
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from './codex-runtime-contract.js';
6
6
  import { codexSchemaSnapshotReport } from './codex-schema-snapshot.js';
7
7
  import { codexHookWarningCheck } from './codex-hook-warning-detector.js';
8
8
  export async function codexCompatibilityReport(opts = {}) {
9
9
  const root = opts.root || await projectRoot();
10
10
  const requiredBaseline = opts.requiredBaseline || opts.require || CODEX_REQUIRED_BASELINE_TAG;
11
11
  const version = await codexVersionReport({ ...opts, requiredBaseline });
12
- const releaseManifest = await codexReleaseManifestParity(root).catch((err) => ({
13
- ok: false,
14
- mismatches: ['manifest_load_failed'],
15
- manifest: null,
16
- manifest_path: null,
17
- manifest_sha256: null,
18
- error: err?.message || String(err)
19
- }));
12
+ const releaseContract = {
13
+ ok: true,
14
+ dependency_source: CURRENT_CODEX_RUNTIME_CONTRACT.dependencySource,
15
+ contract: CURRENT_CODEX_RUNTIME_CONTRACT
16
+ };
20
17
  const snapshot = await codexSchemaSnapshotReport();
21
18
  const hooks = await codexHookWarningCheck(root, { recordWrongness: false });
22
19
  const current = await detectCodexCurrentCapability({
@@ -24,11 +21,11 @@ export async function codexCompatibilityReport(opts = {}) {
24
21
  codexBin: opts.codexBin || null,
25
22
  requireReal: opts.requireReal === true
26
23
  });
27
- const ok = Boolean(version.policy.ok && releaseManifest.ok && snapshot.ok && hooks.ok && current.ok);
24
+ const ok = Boolean(version.policy.ok && snapshot.ok && hooks.ok && current.ok);
28
25
  return {
29
26
  schema: CODEX_COMPAT_SCHEMA,
30
27
  required_baseline: requiredBaseline,
31
- release_manifest: releaseManifest,
28
+ release_contract: releaseContract,
32
29
  detected: version.detected,
33
30
  current_capability: current,
34
31
  capabilities: current.feature_states,
@@ -58,7 +55,6 @@ export async function codexCompatibilityReport(opts = {}) {
58
55
  ],
59
56
  blockers: [
60
57
  ...(version.policy.ok ? [] : ['codex_version_policy_failed']),
61
- ...(releaseManifest.ok ? [] : ['codex_release_manifest_mismatch']),
62
58
  ...(snapshot.ok ? [] : ['codex_hook_schema_snapshot_invalid']),
63
59
  ...(hooks.ok ? [] : ['codex_hook_semantic_warning']),
64
60
  ...current.blockers
@@ -0,0 +1,62 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { packageRoot } from '../fsx.js';
4
+ export const CODEX_RUNTIME_CONTRACT_SCHEMA = 'sks.codex-runtime-contract.v2';
5
+ const sdkVersion = codexSdkDependencyVersion();
6
+ export const CURRENT_CODEX_RUNTIME_CONTRACT = {
7
+ schema: CODEX_RUNTIME_CONTRACT_SCHEMA,
8
+ targetTag: `rust-v${sdkVersion}`,
9
+ requiredCliVersion: sdkVersion,
10
+ preferredCliVersion: sdkVersion,
11
+ sdkVersion,
12
+ minimumSupportedVersion: sdkVersion,
13
+ narutoCapabilityFloorVersion: sdkVersion,
14
+ protocolMode: 'app-server-v2',
15
+ dependencySource: 'package.json#dependencies.@openai/codex-sdk',
16
+ featurePolicies: {
17
+ multiAgentMode: 'delegate',
18
+ multiAgentV2: 'delegate',
19
+ agentsMaxConcurrentThreads: 'delegate',
20
+ indexedWebSearch: 'probe',
21
+ currentTimeRead: 'wrap',
22
+ threadListSearchRead: 'probe',
23
+ pluginCatalogRefresh: 'probe',
24
+ terminalSubagentErrorPropagation: 'probe',
25
+ execMcpTransientRecovery: 'probe',
26
+ remoteNativeEnvironment: 'probe',
27
+ rolloutTokenBudget: 'probe',
28
+ mcpStartupToolTimeouts: 'wrap',
29
+ gpt56TerraLunaSolRouting: 'delegate',
30
+ mcpPaginatedDiscovery: 'wrap',
31
+ portableAgentPlugins: 'delegate',
32
+ automaticApprovalReview: 'delegate'
33
+ },
34
+ requiredRealProbes: [
35
+ 'runtime_identity',
36
+ 'protocol_schema_generation',
37
+ 'multi_agent_mode_schema',
38
+ 'indexed_web_search_schema',
39
+ 'current_time_read_schema',
40
+ 'thread_list_search_schema',
41
+ 'terminal_error_schema',
42
+ 'rollout_budget_schema'
43
+ ],
44
+ supportedPlatforms: [
45
+ 'darwin-arm64',
46
+ 'darwin-x64',
47
+ 'linux-arm64',
48
+ 'linux-x64',
49
+ 'win32-arm64',
50
+ 'win32-x64'
51
+ ]
52
+ };
53
+ export const NARUTO_REQUIRED_CODEX_VERSION = CURRENT_CODEX_RUNTIME_CONTRACT.narutoCapabilityFloorVersion;
54
+ export function codexSdkDependencyVersion(root = packageRoot()) {
55
+ const packagePath = path.join(root, 'package.json');
56
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
57
+ const value = String(pkg.dependencies?.['@openai/codex-sdk'] || '').trim();
58
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value)) {
59
+ throw new Error(`@openai/codex-sdk must be an exact semver in ${packagePath}`);
60
+ }
61
+ return value;
62
+ }
@@ -1,12 +1,12 @@
1
- import { CURRENT_CODEX_RELEASE_MANIFEST } from './codex-release-manifest.js';
1
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from './codex-runtime-contract.js';
2
2
  export const CODEX_COMPAT_SCHEMA = 'sks.codex-compat.v2';
3
- export const CODEX_REQUIRED_BASELINE_TAG = CURRENT_CODEX_RELEASE_MANIFEST.targetTag;
4
- export const CODEX_REQUIRED_VERSION = CURRENT_CODEX_RELEASE_MANIFEST.preferredCliVersion
5
- || CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion;
3
+ export const CODEX_REQUIRED_BASELINE_TAG = CURRENT_CODEX_RUNTIME_CONTRACT.targetTag;
4
+ export const CODEX_REQUIRED_VERSION = CURRENT_CODEX_RUNTIME_CONTRACT.preferredCliVersion
5
+ || CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion;
6
6
  export const CODEX_PREFERRED_VERSION = CODEX_REQUIRED_VERSION;
7
- export const CODEX_MINIMUM_SUPPORTED_VERSION = CURRENT_CODEX_RELEASE_MANIFEST.minimumSupportedVersion;
8
- export const CODEX_HOOK_SCHEMA_BASELINE_TAG = CURRENT_CODEX_RELEASE_MANIFEST.targetTag;
9
- export const CODEX_HOOK_SCHEMA_VERSION = CURRENT_CODEX_RELEASE_MANIFEST.targetTag;
7
+ export const CODEX_MINIMUM_SUPPORTED_VERSION = CURRENT_CODEX_RUNTIME_CONTRACT.minimumSupportedVersion;
8
+ export const CODEX_HOOK_SCHEMA_BASELINE_TAG = 'latest';
9
+ export const CODEX_HOOK_SCHEMA_VERSION = 'latest';
10
10
  const UPDATE_CTA = 'prefer latest Codex CLI via `sks codex update` or Menu Bar / SKS Center → Update Codex CLI Now';
11
11
  export function compareSemverLike(a, b) {
12
12
  const pa = parseVersionParts(a);
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { findCodexBinary } from '../codex-adapter.js';
3
3
  import { compareSemverLike, parseCodexVersionText } from '../codex-compat/codex-version-policy.js';
4
- import { CURRENT_CODEX_RELEASE_MANIFEST } from '../codex-compat/codex-release-manifest.js';
4
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from '../codex-compat/codex-runtime-contract.js';
5
5
  import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
6
6
  export async function detectCodexCurrentAppCapability(input = {}) {
7
7
  const fake = process.env.SKS_CODEX_CURRENT_APP_FAKE === '1';
@@ -9,10 +9,10 @@ export async function detectCodexCurrentAppCapability(input = {}) {
9
9
  ? input.codexBin || process.env.CODEX_BIN || 'codex'
10
10
  : input.codexBin || process.env.CODEX_BIN || await findCodexBinary();
11
11
  const versionText = fake
12
- ? String(process.env.SKS_CODEX_VERSION_FAKE || `codex-cli ${CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion}`)
12
+ ? String(process.env.SKS_CODEX_VERSION_FAKE || `codex-cli ${CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion}`)
13
13
  : await readCodexVersionText(codexBin);
14
14
  const parsed = parseCodexVersion(versionText);
15
- const currentRelease = Boolean(parsed && semverGte(parsed, CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion));
15
+ const currentRelease = Boolean(parsed && semverGte(parsed, CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion));
16
16
  const probeMode = process.env.SKS_CODEX_CURRENT_APP_PROBE === '1' ? 'feature-probe' : 'version-only';
17
17
  const featureProbeResults = probeMode === 'feature-probe'
18
18
  ? await probeCodexCurrentAppFeatures(codexBin, { fake })