sneakoscope 7.3.0 → 7.6.0

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 (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -1,5 +1,77 @@
1
1
  import { exists, runProcess, which } from '../fsx.js';
2
2
  import { inspectCodexLbCliLaunchRecovery } from '../codex-control/codex-lb-launch-recovery.js';
3
+ export async function quitCodexApp(opts = {}) {
4
+ const env = opts.env || process.env;
5
+ const appName = String(opts.appName || env.SKS_CODEX_APP_NAME || 'ChatGPT');
6
+ const bundleId = String(opts.bundleId || env.SKS_CODEX_APP_BUNDLE_ID || 'com.openai.codex');
7
+ if (opts.enabled === false)
8
+ return quitSkipped(appName, 'disabled');
9
+ if ((opts.platform || process.platform) !== 'darwin')
10
+ return quitSkipped(appName, 'not_macos');
11
+ const run = opts.runProcessImpl || runProcess;
12
+ const osascript = opts.osascriptPath
13
+ || await which('osascript').catch(() => null)
14
+ || await exists('/usr/bin/osascript').then((ok) => ok ? '/usr/bin/osascript' : null).catch(() => null);
15
+ if (!osascript) {
16
+ return {
17
+ schema: 'sks.codex-app-quit.v1',
18
+ ok: false,
19
+ status: 'blocked',
20
+ app_name: appName,
21
+ bundle_id: bundleId,
22
+ blockers: ['osascript_missing']
23
+ };
24
+ }
25
+ const appTarget = bundleId ? `application id ${JSON.stringify(bundleId)}` : `application ${JSON.stringify(appName)}`;
26
+ const quit = await run(osascript, ['-e', `tell ${appTarget} to quit`], {
27
+ timeoutMs: 5000,
28
+ maxOutputBytes: 8192
29
+ }).catch((err) => ({
30
+ code: 1,
31
+ stdout: '',
32
+ stderr: err?.message || String(err)
33
+ }));
34
+ const quitOk = quit.code === 0
35
+ || /not running|Can't get application|application isn't running/i.test(String(quit.stderr || quit.stdout || ''));
36
+ const exitTimeoutMs = Math.max(250, Number(opts.exitTimeoutMs ?? env.SKS_CODEX_APP_EXIT_TIMEOUT_MS ?? 5000));
37
+ const pollMs = Math.max(50, Number(opts.pollMs ?? 100));
38
+ let exited = !quitOk;
39
+ let attempts = 0;
40
+ const deadline = Date.now() + exitTimeoutMs;
41
+ while (quitOk && Date.now() < deadline) {
42
+ attempts += 1;
43
+ const probe = await run(osascript, ['-e', `${appTarget} is running`], {
44
+ timeoutMs: 1000,
45
+ maxOutputBytes: 1024
46
+ }).catch(() => null);
47
+ if (probe?.code === 0 && String(probe.stdout || '').trim().toLowerCase() === 'false') {
48
+ exited = true;
49
+ break;
50
+ }
51
+ await sleep(pollMs);
52
+ }
53
+ return {
54
+ schema: 'sks.codex-app-quit.v1',
55
+ ok: quitOk && exited,
56
+ status: quitOk && exited ? 'quit' : 'blocked',
57
+ app_name: appName,
58
+ bundle_id: bundleId,
59
+ quit: {
60
+ ok: quitOk,
61
+ code: quit.code,
62
+ error: quitOk ? null : String(quit.stderr || quit.stdout || '').trim()
63
+ },
64
+ exit_wait: {
65
+ ok: exited,
66
+ attempts,
67
+ error: exited ? null : 'codex_app_exit_timeout'
68
+ },
69
+ blockers: [
70
+ ...(quitOk ? [] : ['codex_app_quit_failed']),
71
+ ...(exited ? [] : ['codex_app_exit_timeout'])
72
+ ]
73
+ };
74
+ }
3
75
  export async function restartCodexApp(opts = {}) {
4
76
  const env = opts.env || process.env;
5
77
  const appName = String(opts.appName || env.SKS_CODEX_APP_NAME || 'ChatGPT');
@@ -110,6 +182,17 @@ function skipped(appName, reason) {
110
182
  blockers: []
111
183
  };
112
184
  }
185
+ function quitSkipped(appName, reason) {
186
+ return {
187
+ schema: 'sks.codex-app-quit.v1',
188
+ ok: true,
189
+ status: 'skipped',
190
+ skipped: true,
191
+ reason,
192
+ app_name: appName,
193
+ blockers: []
194
+ };
195
+ }
113
196
  function sleep(ms) {
114
197
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
115
198
  }
@@ -325,12 +325,16 @@ function hasActiveCodexLbProviderSelectionSignals(signals) {
325
325
  const selected = signals.some((signal) => signal.key_path === 'model_provider' && signal.value_preview === 'codex-lb');
326
326
  if (!selected)
327
327
  return false;
328
- const has = (key, value) => signals.some((signal) => signal.key_path === key && signal.value_preview === value);
328
+ const has = (key, value) => signals.some((signal) => (signal.key_path === key && signal.value_preview.toLowerCase() === value));
329
329
  const present = (key) => signals.some((signal) => signal.key_path === key);
330
- return has('model_providers.codex-lb.name', 'openai')
331
- && has('model_providers.codex-lb.wire_api', 'responses')
332
- && present('model_providers.codex-lb.env_key')
330
+ const cliProviderAuth = present('model_providers.codex-lb.env_key')
331
+ && present('model_providers.codex-lb.requires_openai_auth');
332
+ const desktopCompatAuth = present('model_providers.codex-lb.env_http_headers')
333
333
  && present('model_providers.codex-lb.requires_openai_auth');
334
+ return (has('model_providers.codex-lb.name', 'openai') || has('model_providers.codex-lb.name', 'codex-lb'))
335
+ && present('model_providers.codex-lb.base_url')
336
+ && has('model_providers.codex-lb.wire_api', 'responses')
337
+ && (cliProviderAuth || desktopCompatAuth);
334
338
  }
335
339
  function redactValuePreview(keyPath, value) {
336
340
  if (SECRET_KEY_RE.test(keyPath))
@@ -139,7 +139,7 @@ export async function restoreDesktopRoutingSnapshot(input) {
139
139
  status: 'snapshot_missing',
140
140
  blockers: ['desktop_routing_snapshot_missing'],
141
141
  warnings: [],
142
- hint: 'No previous Desktop routing snapshot. Use sks codex-lb use-codex-lb or Restore Chat / Pro.'
142
+ hint: 'No previous Desktop routing snapshot. Use `sks codex-lb use-desktop-full` to enable managed routing or `sks codex-lb disable` to remove only SKS-managed routing.'
143
143
  };
144
144
  }
145
145
  const configPath = input.configPath || path.join(codexHomePath({ home, env }), 'config.toml');
@@ -17,13 +17,18 @@ export const NATIVE_SOURCE_FILES = [
17
17
  'SidebarItem.swift',
18
18
  'ControlKit.swift',
19
19
  'OverviewViewController.swift',
20
+ 'OverviewSummary.swift',
20
21
  'UpdatesViewController.swift',
21
22
  'MCPServersViewController.swift',
22
23
  'ProvidersViewController.swift',
24
+ 'ProvidersRoutingTruth.swift',
23
25
  'ProvidersOpenRouter.swift',
24
26
  'ProvidersRoleModels.swift',
25
27
  'ProvidersMultiProvider.swift',
28
+ 'TelegramHubSupervisor.swift',
26
29
  'RemoteTelegramViewController.swift',
30
+ 'RemoteTelegramCenterState.swift',
31
+ 'RemoteTelegramRendering.swift',
27
32
  'DiagnosticsViewController.swift',
28
33
  'SettingsViewController.swift',
29
34
  'OperationCoordinator.swift',
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { readJson, writeJsonAtomic } from '../../fsx.js';
4
+ export { messageOf as errorMessage } from '../../errors/message.js';
4
5
  export const JOURNAL_SCHEMA = 'sks.menubar-generation-transaction.v1';
5
6
  const OUTCOME_SCHEMA = 'sks.menubar-generation-transaction-outcome.v1';
6
7
  export async function loadJournal(input) {
@@ -87,9 +88,6 @@ export async function outcomeFromUnknownJournal(purpose, journalPath, status, er
87
88
  export async function existsNoFollow(file) {
88
89
  return fs.lstat(file).then(() => true).catch(() => false);
89
90
  }
90
- export function errorMessage(error) {
91
- return error instanceof Error ? error.message : String(error);
92
- }
93
91
  async function pairOutcome(pair) {
94
92
  return {
95
93
  kind: pair.kind,
@@ -73,6 +73,24 @@ export async function installSksMenuBar(opts = {}) {
73
73
  cleanup = await cleanupMacLaunchSecretEnvironment({ env });
74
74
  if (!cleanup.ok)
75
75
  warnings.push('launch_secret_env_cleanup_incomplete');
76
+ try {
77
+ const { syncDesktopCenterLaunchCredentials } = await import('../../codex-lb/desktop-center-credentials.js');
78
+ const { CODEX_LB_DESKTOP_COMPAT_MARKER } = await import('../../../cli/install-helpers-codex-lb-config.js');
79
+ const { readText } = await import('../../fsx.js');
80
+ const configText = await readText(path.join(paths.home, '.codex', 'config.toml'), '');
81
+ if (configText.includes(CODEX_LB_DESKTOP_COMPAT_MARKER)) {
82
+ const restored = await syncDesktopCenterLaunchCredentials({
83
+ mode: 'desktop-dual-auth-compat',
84
+ home: paths.home
85
+ });
86
+ if (!restored.ok)
87
+ warnings.push('desktop_compat_launch_env_restore_incomplete');
88
+ else
89
+ actions.push('restored Center Codex LB credentials into Desktop launch environment');
90
+ }
91
+ }
92
+ catch {
93
+ }
76
94
  const swiftc = env.SKS_MENUBAR_SWIFTC || await which('swiftc').catch(() => null) || '/usr/bin/swiftc';
77
95
  const codesign = env.SKS_MENUBAR_CODESIGN || await which('codesign').catch(() => null) || '/usr/bin/codesign';
78
96
  const launchctl = env.SKS_MENUBAR_LAUNCHCTL || await which('launchctl').catch(() => null) || '/bin/launchctl';
@@ -254,11 +254,6 @@ export async function useMultiProviderRouter(input) {
254
254
  hint: OPENCODEX_DESIGN_B_FORCE_HINT
255
255
  };
256
256
  }
257
- const unselect = await unselectCodexLbProvider({
258
- home,
259
- configPath,
260
- allowActiveSharedAuthTransition: true
261
- }).catch((err) => ({ ok: false, status: 'failed', provider_error: err?.message || String(err) }));
262
257
  const providerBody = tomlTableBody(current, `model_providers.${MULTI_PROVIDER_ROUTER_ID}`);
263
258
  const existingProviderName = tomlString(providerBody, 'name');
264
259
  if (providerBody && existingProviderName !== MULTI_PROVIDER_ROUTER_NAME) {
@@ -285,6 +280,26 @@ export async function useMultiProviderRouter(input) {
285
280
  hint: 'Re-run with --replace-catalog only if replacing the current user model catalog is intentional.'
286
281
  };
287
282
  }
283
+ const unselect = await unselectCodexLbProvider({
284
+ home,
285
+ configPath,
286
+ processEnv: env
287
+ }).catch((err) => ({ ok: false, status: 'failed', provider_error: err?.message || String(err) }));
288
+ if (unselect?.ok !== true) {
289
+ return {
290
+ ...routerBlocked('sks.codex-app-use-multi-provider-router.v1', unselect?.reason === 'shared_codex_lb_auth_active'
291
+ ? 'legacy_codex_lb_desktop_config_requires_migration'
292
+ : String(unselect?.status || 'codex_lb_unselect_failed')),
293
+ model,
294
+ base_url: baseUrlResult.value,
295
+ catalog_path: catalogPath,
296
+ unselect,
297
+ guidance: unselect?.reason === 'shared_codex_lb_auth_active'
298
+ ? ['Run: sks codex-lb migrate-legacy-desktop --restart-app']
299
+ : []
300
+ };
301
+ }
302
+ const currentAfterUnselect = await readText(configPath, '');
288
303
  const providerBlock = [
289
304
  `[model_providers.${MULTI_PROVIDER_ROUTER_ID}]`,
290
305
  `name = ${JSON.stringify(MULTI_PROVIDER_ROUTER_NAME)}`,
@@ -292,13 +307,13 @@ export async function useMultiProviderRouter(input) {
292
307
  'wire_api = "responses"',
293
308
  'requires_openai_auth = false'
294
309
  ].join('\n');
295
- let next = upsertTomlTable(current, `model_providers.${MULTI_PROVIDER_ROUTER_ID}`, providerBlock);
310
+ let next = upsertTomlTable(currentAfterUnselect, `model_providers.${MULTI_PROVIDER_ROUTER_ID}`, providerBlock);
296
311
  next = upsertTopLevelTomlString(next, 'model_catalog_json', catalogPath);
297
312
  next = upsertTopLevelTomlString(next, 'model_provider', MULTI_PROVIDER_ROUTER_ID);
298
313
  next = upsertTopLevelTomlString(next, 'model', model);
299
314
  next = normalizeCodexFastModeUiConfig(next);
300
315
  next = ensureTrailingNewline(next);
301
- const write = await safeWriteCodexConfigToml(configPath, current, next, 'multi-provider-router-use', {
316
+ const write = await safeWriteCodexConfigToml(configPath, currentAfterUnselect, next, 'multi-provider-router-use', {
302
317
  verifyUnchangedBeforeWrite: true,
303
318
  expectedBeforeExists: configExistedBefore
304
319
  });
@@ -365,9 +380,6 @@ export async function useMultiProviderRouter(input) {
365
380
  ...catalog.warnings,
366
381
  ...ownership.warnings,
367
382
  ...cache.warnings,
368
- ...(unselect?.ok === false
369
- ? [`codex_lb_unselect:${unselect.provider_error || unselect.status}`]
370
- : []),
371
383
  ...(catalog.models.find((entry) => entry.model === model)?.multi_agent_version === 'v2'
372
384
  ? []
373
385
  : ['multi_provider_router_model_multi_agent_v2_missing']),
@@ -188,8 +188,33 @@ export async function useOpenRouter(input) {
188
188
  const unselect = await unselectCodexLbProvider({
189
189
  home,
190
190
  configPath,
191
- allowActiveSharedAuthTransition: true
191
+ processEnv: env
192
192
  }).catch((err) => ({ ok: false, status: 'failed', provider_error: err?.message || String(err) }));
193
+ if (unselect?.ok !== true) {
194
+ const sharedAuthActive = unselect?.reason === 'shared_codex_lb_auth_active';
195
+ return {
196
+ schema: 'sks.codex-app-use-openrouter.v1',
197
+ generated_at: nowIso(),
198
+ ok: false,
199
+ status: 'blocked',
200
+ mode: 'openrouter',
201
+ model,
202
+ profile: activationProfile,
203
+ unselect,
204
+ routing_snapshot: routingSnapshot,
205
+ routing_snapshot_write: snapshotWrite,
206
+ thread_visibility: threadVisibility,
207
+ blockers: [
208
+ sharedAuthActive
209
+ ? 'legacy_codex_lb_desktop_config_requires_migration'
210
+ : String(unselect?.status || 'codex_lb_unselect_failed')
211
+ ],
212
+ warnings: [],
213
+ ...(sharedAuthActive
214
+ ? { guidance: ['Run: sks codex-lb migrate-legacy-desktop --restart-app'] }
215
+ : {})
216
+ };
217
+ }
193
218
  const modelCatalog = await writeOpenRouterManagedCatalog({ model, home, env });
194
219
  const current = await readText(configPath, '');
195
220
  const catalogBind = openRouterCatalogBindDecision(current, { home, env, configPath });
@@ -9,8 +9,8 @@ import { PRODUCT_DESIGN_AUTO_INSTALL_ENV, ensureProductDesignPluginInstalled, pr
9
9
  import { GLM_CODEX_CONFIG_PROVIDER_ID, GLM_52_OPENROUTER_MODEL, RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS } from './codex-app/openrouter-provider.js';
10
10
  import { sksOpenRouterCatalogPath } from './codex-app/codex-model-catalog.js';
11
11
  import { resolveOpenRouterApiKey } from './providers/openrouter/openrouter-secret-store.js';
12
- import { codexLbEnvPath, loadCodexLbEnv, parseShellEnvValue, readCodexLbModelCatalog } from './codex-lb/codex-lb-env.js';
13
- import { codexLbToolCatalogPath, inspectCodexLbToolCatalog } from './codex-lb/codex-lb-tool-catalog.js';
12
+ import { codexLbEnvPath, codexLbMetadataPath, loadCodexLbEnv, parseShellEnvValue, readCodexLbModelCatalog } from './codex-lb/codex-lb-env.js';
13
+ import { codexLbToolCatalogPath, inspectCodexLbToolCatalog, shouldBindLocalModelCatalog } from './codex-lb/codex-lb-tool-catalog.js';
14
14
  import { isSksOwnedGlobalUiLock } from './codex-app/codex-app-ui-state-snapshot.js';
15
15
  import { redactString } from './secret-redaction.js';
16
16
  import { escapeRegExp } from './text/regex.js';
@@ -169,7 +169,7 @@ export async function codexAppIntegrationStatus(opts = {}) {
169
169
  });
170
170
  const pluginPickerReady = requiredFeatureFlags.tool_suggest && requiredFeatureFlags.plugins && requiredFeatureFlags.apps && defaultPlugins.ok && pluginSkillShadows.ok && fastModeConfig.ok;
171
171
  const gitActions = codexGitActionReadiness({ requiredFeatureFlags, remoteControl });
172
- const selectedProviderReady = providerModelUi.selected_provider_ok !== false;
172
+ const selectedProviderReady = providerModelUi.effective_ready === true;
173
173
  const ready = appInstalled && Boolean(codex.bin) && mcpList.ok && featureList.ok && requiredFeatureFlagsOk && pluginPickerReady && fastModeConfig.ok && selectedProviderReady && imageGenerationReady && gitActions.ok && computerUseReady && browserToolReady && chromeExtension.ok;
174
174
  return {
175
175
  ok: ready,
@@ -204,6 +204,7 @@ export async function codexAppIntegrationStatus(opts = {}) {
204
204
  fast_mode_config: fastModeConfig,
205
205
  provider_model_ui: providerModelUi,
206
206
  selected_provider_ready: selectedProviderReady,
207
+ codex_lb_capabilities: opts.codexLbCapabilityReport || null,
207
208
  git_actions: gitActions,
208
209
  image_generation: imageGenerationReady,
209
210
  image_generation_source: imageGenerationReady ? 'codex_features_list' : 'missing',
@@ -472,8 +473,8 @@ function codexNativeSksMenuStatus() {
472
473
  companion_install_command: 'sks doctor --fix',
473
474
  alternatives: [
474
475
  'sks doctor --fix',
475
- 'sks codex-lb use-codex-lb',
476
- 'sks codex-lb use-oauth',
476
+ 'sks codex-lb use-desktop-full',
477
+ 'sks codex-lb disable',
477
478
  'sks codex-app set-openrouter-key --api-key-stdin',
478
479
  'codex://settings'
479
480
  ],
@@ -568,7 +569,7 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, re
568
569
  }
569
570
  if (!gitActions?.ok) {
570
571
  lines.push(`Codex App git commit/push actions are blocked: ${gitActions?.blockers?.join(', ') || 'git action readiness'}. The app Commit, Push, Commit and Push, and PR flows need codex_git_commit, hooks, and Codex CLI remote-control support.`);
571
- lines.push(`Run: sks doctor --fix; if remote-control is still blocked, update Codex CLI to ${CODEX_REMOTE_CONTROL_MIN_VERSION}+ and restart older app-server/TUI sessions.`);
572
+ lines.push('Run: sks doctor --fix; if remote-control is still blocked, update Codex CLI to the official latest stable release and restart older app-server/TUI sessions.');
572
573
  }
573
574
  else {
574
575
  lines.push('Codex App git actions are enabled for Commit, Push, Commit and Push, and PR flows; SKS hooks treat those app metadata actions as lightweight git UI actions.');
@@ -577,7 +578,7 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, re
577
578
  lines.push('Codex App does not expose a documented third-party API for inserting SKS between Window and Help, so SKS uses a right-side macOS menu bar companion instead. Run `sks doctor --fix` to install or refresh it.');
578
579
  }
579
580
  if (appInstalled && (!computerUseReady || !browserToolReady)) {
580
- lines.push('Open Codex App settings and enable recommended MCP/plugin tools. Codex CLI 0.130.0+ remote-control/app-server sessions can pick up config changes live; restart older CLI/TUI sessions.');
581
+ lines.push('Open Codex App settings and enable recommended MCP/plugin tools. Codex CLI sessions that expose remote-control/app-server can pick up config changes live; restart older CLI/TUI sessions.');
581
582
  lines.push(`Required for SKS web QA/UX/browser evidence: Codex Chrome Extension first (${CODEX_CHROME_EXTENSION_SETUP_DOCS_URL}). Computer Use is reserved for native Mac/non-web surfaces.`);
582
583
  lines.push('Verify with: codex features list; codex mcp list');
583
584
  }
@@ -939,13 +940,28 @@ export async function codexProviderModelUiStatus(opts = {}) {
939
940
  }
940
941
  const codexLbProvider = tomlTableAny(configText, ['model_providers.codex-lb', 'model_providers."codex-lb"']);
941
942
  const codexLbProviderPresent = Boolean(codexLbProvider);
942
- const codexLbProviderContractOk = codexLbProviderPresent
943
- && hasTomlString(codexLbProvider, 'name', 'openai')
943
+ const codexLbCliProviderContractOk = codexLbProviderPresent
944
+ && hasTomlString(codexLbProvider, 'name', 'codex-lb')
944
945
  && hasTomlString(codexLbProvider, 'wire_api', 'responses')
945
946
  && hasTomlString(codexLbProvider, 'env_key', 'CODEX_LB_API_KEY')
946
947
  && hasTomlBoolean(codexLbProvider, 'supports_websockets', true)
947
- && hasTomlBoolean(codexLbProvider, 'requires_openai_auth', true);
948
+ && hasTomlBoolean(codexLbProvider, 'requires_openai_auth', false);
949
+ const codexLbCompatProviderContractOk = codexLbProviderPresent
950
+ && hasTomlString(codexLbProvider, 'name', 'OpenAI')
951
+ && hasTomlString(codexLbProvider, 'wire_api', 'responses')
952
+ && hasTomlBoolean(codexLbProvider, 'supports_websockets', true)
953
+ && hasTomlBoolean(codexLbProvider, 'requires_openai_auth', true)
954
+ && !hasTomlKey(codexLbProvider, 'env_key')
955
+ && hasCodexLbGatewayEnvHeader(codexLbProvider);
948
956
  const codexLbSelectedDefault = /(?:^|\n)\s*model_provider\s*=\s*"codex-lb"\s*(?:#.*)?(?=\n|$)/.test(topLevelToml(globalConfig));
957
+ const codexLbDesktopMode = opts.codexLbDesktopMode
958
+ || inferCodexLbDesktopModeForUi(globalConfig);
959
+ const codexLbProviderContractOk = codexLbDesktopMode === 'desktop-dual-auth-compat'
960
+ ? codexLbCompatProviderContractOk
961
+ : codexLbCliProviderContractOk;
962
+ const metadata = await readJsonIfExists(opts.codexLbMetadataPath || codexLbMetadataPath(home));
963
+ const gatewayAuthTransport = normalizeCodexLbGatewayAuthTransportForUi(opts.codexLbGatewayAuthTransport || metadata?.gateway_auth_transport);
964
+ const localCatalogBindingAllowed = shouldBindLocalModelCatalog(codexLbDesktopMode);
949
965
  const selectedProviderValue = topLevelToml(globalConfig)
950
966
  .match(/(?:^|\n)\s*model_provider\s*=\s*"([^"]+)"\s*(?:#.*)?(?=\n|$)/)?.[1] || null;
951
967
  const managedCatalogPath = codexLbToolCatalogPath(path.join(home || '', '.codex'));
@@ -953,13 +969,18 @@ export async function codexProviderModelUiStatus(opts = {}) {
953
969
  const managedCatalogConfigured = Boolean(configuredCatalogPath)
954
970
  && path.resolve(configuredCatalogPath) === path.resolve(managedCatalogPath);
955
971
  let persistedCatalog = null;
956
- if (!opts.codexLbModelCatalog && managedCatalogConfigured) {
972
+ if (!opts.codexLbModelCatalog && managedCatalogConfigured && localCatalogBindingAllowed) {
957
973
  persistedCatalog = await inspectCodexLbToolCatalog(managedCatalogPath).catch(() => null);
958
974
  }
959
975
  let liveCatalog = opts.codexLbModelCatalog || null;
960
976
  if (!liveCatalog && !persistedCatalog?.ok && codexLbProviderContractOk && codexLbApiKeySource !== 'missing' && codexLbBaseUrlSource !== 'missing') {
961
977
  const loadedEnv = await loadCodexLbEnv({ home, envPath: codexLbEnvFilePath }).catch(() => null);
962
- liveCatalog = loadedEnv ? await readCodexLbModelCatalog({ loadedEnv }).catch(() => null) : null;
978
+ liveCatalog = loadedEnv
979
+ ? await readCodexLbModelCatalog({
980
+ loadedEnv,
981
+ gatewayAuthTransport
982
+ }).catch(() => null)
983
+ : null;
963
984
  }
964
985
  const codexLbModelCatalog = opts.codexLbModelCatalog || (persistedCatalog?.ok
965
986
  ? {
@@ -979,9 +1000,8 @@ export async function codexProviderModelUiStatus(opts = {}) {
979
1000
  source: 'persisted_model_catalog_json'
980
1001
  }
981
1002
  : null);
982
- const expectedCodexLbModels = ['gpt-5.6-luna', 'gpt-5.6-terra', 'gpt-5.6-sol'];
983
1003
  const catalogModels = Array.isArray(codexLbModelCatalog?.models) ? codexLbModelCatalog.models.map(String) : [];
984
- const expectedCodexLbModelsPresent = expectedCodexLbModels.every((model) => catalogModels.includes(model));
1004
+ const advertisedCodexLbModelsPresent = codexLbModelCatalog?.ok === true && catalogModels.length > 0;
985
1005
  const glmBlockers = [
986
1006
  ...(glmProviderPresent ? [] : ['glm_openrouter_provider_missing']),
987
1007
  ...glmProfileBlockers,
@@ -992,11 +1012,14 @@ export async function codexProviderModelUiStatus(opts = {}) {
992
1012
  ...(codexLbProviderPresent && !codexLbProviderContractOk ? ['codex_lb_provider_contract_drift'] : []),
993
1013
  ...(codexLbApiKeySource !== 'missing' ? [] : ['codex_lb_api_key_missing']),
994
1014
  ...(codexLbBaseUrlSource !== 'missing' ? [] : ['codex_lb_base_url_missing']),
995
- ...(codexLbSelectedDefault && !managedCatalogConfigured && !expectedCodexLbModelsPresent
1015
+ ...(codexLbDesktopMode === 'desktop-native-bridge' && managedCatalogConfigured
1016
+ ? ['native_bridge_local_catalog_replacement_forbidden']
1017
+ : []),
1018
+ ...(codexLbSelectedDefault && localCatalogBindingAllowed && !managedCatalogConfigured && !advertisedCodexLbModelsPresent
996
1019
  ? ['codex_lb_model_catalog_json_unselected']
997
1020
  : []),
998
- ...(codexLbProviderContractOk && codexLbApiKeySource !== 'missing' && codexLbBaseUrlSource !== 'missing' && !expectedCodexLbModelsPresent
999
- ? ['codex_lb_gpt_5_6_catalog_unverified']
1021
+ ...(codexLbProviderContractOk && codexLbApiKeySource !== 'missing' && codexLbBaseUrlSource !== 'missing' && !advertisedCodexLbModelsPresent
1022
+ ? ['codex_lb_model_catalog_unverified']
1000
1023
  : [])
1001
1024
  ];
1002
1025
  const openRouterSelectedDefault = selectedProviderValue === GLM_CODEX_CONFIG_PROVIDER_ID;
@@ -1027,17 +1050,38 @@ export async function codexProviderModelUiStatus(opts = {}) {
1027
1050
  const installCommand = 'sks codex-app use-openrouter --model z-ai/glm-5.2';
1028
1051
  const setupCommand = 'sks codex-lb setup --host <domain> --api-key-stdin --yes';
1029
1052
  const setKeyCommand = 'sks codex-lb set-key --api-key-stdin';
1053
+ const desktopPickerEvidence = opts.desktopPickerEvidence || opts.codexLbCapabilityReport?.model_picker || null;
1054
+ const desktopPickerVerified = desktopPickerEvidence?.state === 'verified'
1055
+ || desktopPickerEvidence?.verified === true;
1056
+ const selectedProviderConfigured = selectedProviderBlockers.length === 0;
1057
+ const selectedProviderAdvertised = codexLbSelectedDefault
1058
+ ? advertisedCodexLbModelsPresent
1059
+ : true;
1060
+ const effectiveReady = selectedProviderConfigured && selectedProviderAdvertised && desktopPickerVerified;
1030
1061
  return {
1031
1062
  schema: 'sks.codex-app-provider-model-ui.v1',
1032
- ok: selectedProviderBlockers.length === 0,
1033
- selected_provider_ok: selectedProviderBlockers.length === 0,
1063
+ ok: effectiveReady,
1064
+ selected_provider_ok: effectiveReady,
1065
+ configured: selectedProviderConfigured,
1066
+ advertised: selectedProviderAdvertised,
1067
+ effective_ready: effectiveReady,
1034
1068
  selected_provider: codexLbSelectedDefault ? 'codex-lb' : (selectedProviderValue || 'oauth'),
1035
1069
  selected_provider_blockers: selectedProviderBlockers,
1036
1070
  optional_provider_blockers: optionalProviderBlockers,
1037
1071
  checked: true,
1038
1072
  verification_scope: 'config_and_provider_catalog',
1039
- desktop_picker_verified: false,
1040
- status: selectedProviderBlockers.length === 0 ? 'ready' : 'setup_required',
1073
+ desktop_picker_verified: desktopPickerVerified,
1074
+ desktop_picker_evidence: desktopPickerEvidence,
1075
+ readiness_state: selectedProviderBlockers.length > 0
1076
+ ? 'blocked'
1077
+ : effectiveReady
1078
+ ? 'verified'
1079
+ : 'available_unverified',
1080
+ status: selectedProviderBlockers.length > 0
1081
+ ? 'setup_required'
1082
+ : effectiveReady
1083
+ ? 'ready'
1084
+ : 'available_unverified',
1041
1085
  optional_provider_status: optionalProviderBlockers.length === 0 ? 'ready' : 'setup_available',
1042
1086
  config_paths: {
1043
1087
  codex_config: globalConfigPath,
@@ -1066,6 +1110,9 @@ export async function codexProviderModelUiStatus(opts = {}) {
1066
1110
  key_entry_visible: true,
1067
1111
  provider_present: codexLbProviderPresent,
1068
1112
  provider_contract_ok: codexLbProviderContractOk,
1113
+ provider_contract: codexLbDesktopMode === 'desktop-dual-auth-compat'
1114
+ ? 'desktop-dual-auth-compat'
1115
+ : 'cli-provider',
1069
1116
  selected_default: codexLbSelectedDefault,
1070
1117
  key_present: codexLbApiKeySource !== 'missing',
1071
1118
  api_key_source: codexLbApiKeySource,
@@ -1074,11 +1121,15 @@ export async function codexProviderModelUiStatus(opts = {}) {
1074
1121
  model_catalog_checked: Boolean(codexLbModelCatalog),
1075
1122
  model_catalog_ok: codexLbModelCatalog?.ok === true,
1076
1123
  model_catalog_source: codexLbModelCatalog?.source || null,
1124
+ desktop_mode: codexLbDesktopMode,
1125
+ gateway_auth_transport: gatewayAuthTransport,
1126
+ local_catalog_binding_allowed: localCatalogBindingAllowed,
1077
1127
  model_catalog_json_configured: managedCatalogConfigured,
1078
1128
  model_catalog_json_path: managedCatalogConfigured ? managedCatalogPath : configuredCatalogPath || null,
1079
1129
  model_catalog_models: catalogModels,
1080
- expected_models: expectedCodexLbModels,
1081
- expected_models_present: expectedCodexLbModelsPresent,
1130
+ expected_models: [],
1131
+ expected_models_present: advertisedCodexLbModelsPresent,
1132
+ advertised_models_present: advertisedCodexLbModelsPresent,
1082
1133
  tools_transport: codexLbModelCatalog?.tools_transport || null,
1083
1134
  model_catalog_blockers: codexLbModelCatalog?.blockers || [],
1084
1135
  setup_command: setupCommand,
@@ -1104,6 +1155,44 @@ async function readTextIfExists(file) {
1104
1155
  return '';
1105
1156
  }
1106
1157
  }
1158
+ async function readJsonIfExists(file) {
1159
+ try {
1160
+ const parsed = JSON.parse(await fsp.readFile(file, 'utf8'));
1161
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
1162
+ ? parsed
1163
+ : null;
1164
+ }
1165
+ catch {
1166
+ return null;
1167
+ }
1168
+ }
1169
+ function inferCodexLbDesktopModeForUi(config) {
1170
+ const topLevel = topLevelToml(config);
1171
+ const selected = topLevel.match(/(?:^|\n)\s*model_provider\s*=\s*"([^"]+)"\s*(?:#.*)?(?=\n|$)/)?.[1] || '';
1172
+ const openAiBaseUrl = topLevel.match(/(?:^|\n)\s*openai_base_url\s*=\s*"([^"]+)"\s*(?:#.*)?(?=\n|$)/)?.[1] || '';
1173
+ if (config.includes('# sks-codex-lb-managed-desktop-bridge')
1174
+ && /^http:\/\/(?:127\.0\.0\.1|\[::1\]):\d+\/backend-api\/codex\/?$/i.test(openAiBaseUrl)) {
1175
+ return 'desktop-native-bridge';
1176
+ }
1177
+ if (config.includes('# sks-codex-lb-managed-desktop-compat')
1178
+ && selected === 'codex-lb') {
1179
+ return 'desktop-dual-auth-compat';
1180
+ }
1181
+ return tomlTableAny(config, ['model_providers.codex-lb', 'model_providers."codex-lb"'])
1182
+ ? 'cli-provider'
1183
+ : 'disabled';
1184
+ }
1185
+ function normalizeCodexLbGatewayAuthTransportForUi(value) {
1186
+ return value === 'authorization-bearer-compat'
1187
+ ? 'authorization-bearer-compat'
1188
+ : 'x-codex-lb-api-key';
1189
+ }
1190
+ function hasTomlKey(text, key) {
1191
+ return new RegExp(`(?:^|\\n)\\s*${escapeRegExp(key)}\\s*=`).test(text);
1192
+ }
1193
+ function hasCodexLbGatewayEnvHeader(text) {
1194
+ return /(?:^|\n)\s*env_http_headers\s*=\s*\{[^}\n]*"X-Codex-LB-API-Key"\s*=\s*"CODEX_LB_API_KEY"[^}\n]*\}\s*(?:#.*)?(?=\n|$)/.test(text);
1195
+ }
1107
1196
  async function findDefaultPluginSource(plugin, { home, configText }) {
1108
1197
  const cached = await findPluginCache(plugin.name, { home });
1109
1198
  if (cached)
@@ -1165,6 +1254,9 @@ function providerModelUiSummary(status = {}) {
1165
1254
  : '';
1166
1255
  return ` ${selected} ready${optionalSetup} (live picker reload required)`;
1167
1256
  }
1257
+ if (status.configured && status.readiness_state === 'available_unverified') {
1258
+ return ` ${selected} configured, Desktop picker unverified`;
1259
+ }
1168
1260
  return ` ${selected} setup ${(status.selected_provider_blockers || status.blockers || []).join(', ') || 'required'}`;
1169
1261
  }
1170
1262
  function sksNativeMenuSummary(status = {}) {
@@ -1226,10 +1318,10 @@ function hasTomlBoolean(text = '', key = '', value) {
1226
1318
  }
1227
1319
  function remoteControlGuidance(status = {}) {
1228
1320
  if (!status.codex_cli?.ok)
1229
- return 'Codex remote-control requires Codex CLI 0.130.0+. Install with: npm i -g @openai/codex@latest';
1321
+ return 'Codex remote-control needs a Codex CLI that exposes the app-server entrypoint. Install the official latest stable release: npm i -g @openai/codex@latest';
1230
1322
  if (status.reason === 'codex_cli_version_unknown')
1231
- return 'Codex remote-control requires Codex CLI 0.130.0+, but the installed CLI version could not be parsed. Check: codex --version';
1232
- return `Codex remote-control requires Codex CLI ${CODEX_REMOTE_CONTROL_MIN_VERSION}+. Update with: npm i -g @openai/codex@latest`;
1323
+ return 'Codex remote-control needs a Codex CLI that exposes the app-server entrypoint, but the installed CLI version could not be parsed. Check: codex --version';
1324
+ return 'The installed Codex CLI does not expose remote-control. Update to the official latest stable release: npm i -g @openai/codex@latest';
1233
1325
  }
1234
1326
  function codexCliVersionNumber(versionText = '') {
1235
1327
  const match = String(versionText || '').match(/(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);