sneakoscope 8.1.2 → 8.2.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 (261) hide show
  1. package/README.md +96 -58
  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 -51
  5. package/dist/cli/command-manifest-lite.js +2 -1
  6. package/dist/cli/command-registry.js +2 -1
  7. package/dist/cli/global-mode-router.js +1 -1
  8. package/dist/cli/install-helpers-codex-lb-config.js +112 -914
  9. package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
  10. package/dist/cli/install-helpers-install-support.js +4 -20
  11. package/dist/cli/install-helpers.js +36 -2716
  12. package/dist/cli/router.js +9 -9
  13. package/dist/commands/bridge-command-output.js +111 -0
  14. package/dist/commands/bridge.js +320 -0
  15. package/dist/commands/codex-app.js +39 -123
  16. package/dist/commands/doctor-helpers.js +380 -0
  17. package/dist/commands/doctor-profile.js +3 -0
  18. package/dist/commands/doctor.js +114 -873
  19. package/dist/commands/mad-sks.js +0 -4
  20. package/dist/config/skills-manifest.json +1 -1
  21. package/dist/core/agents/agent-effort-policy.js +11 -21
  22. package/dist/core/agents/agent-recursion-guard.js +26 -0
  23. package/dist/core/agents/agent-runner-codex-exec.js +7 -9
  24. package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
  25. package/dist/core/architecture-hardening/state/state-service.js +49 -13
  26. package/dist/core/auto-review.js +15 -9
  27. package/dist/core/codex/codex-config-guard.js +8 -0
  28. package/dist/core/codex-adapter.js +8 -10
  29. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
  30. package/dist/core/codex-app/codex-app-launcher.js +10 -12
  31. package/dist/core/codex-app/codex-app-restart.js +10 -12
  32. package/dist/core/codex-app/menubar/constants.js +2 -5
  33. package/dist/core/codex-app/menubar/global-install.js +3 -2
  34. package/dist/core/codex-app/menubar/installer/result.js +23 -0
  35. package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
  36. package/dist/core/codex-app/menubar/installer.js +5 -96
  37. package/dist/core/codex-app/menubar/migration.js +1 -42
  38. package/dist/core/codex-app/openrouter-provider.js +0 -78
  39. package/dist/core/codex-app.js +97 -360
  40. package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
  41. package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
  42. package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
  43. package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
  44. package/dist/core/codex-control/codex-task-runner.js +16 -60
  45. package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
  46. package/dist/core/codex-exec-output-schema.js +11 -13
  47. package/dist/core/codex-lb/bridge-contracts.js +2 -0
  48. package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
  49. package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
  50. package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
  51. package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
  52. package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
  53. package/dist/core/codex-lb/capability-runner.js +505 -201
  54. package/dist/core/codex-lb/codex-lb-env.js +179 -12
  55. package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
  56. package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
  57. package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
  58. package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
  59. package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
  60. package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
  61. package/dist/core/codex-lb/combined-catalog.js +12 -0
  62. package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
  63. package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
  64. package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
  65. package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
  66. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
  67. package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
  68. package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
  69. package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
  70. package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
  71. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
  72. package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
  73. package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
  74. package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
  75. package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
  76. package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
  77. package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
  78. package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
  79. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
  80. package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
  81. package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
  82. package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
  83. package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
  84. package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
  85. package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
  86. package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
  87. package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
  88. package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
  89. package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
  90. package/dist/core/codex-lb/desktop-controller.js +2 -1730
  91. package/dist/core/codex-lb/desktop-service.js +462 -627
  92. package/dist/core/codex-lb/migration-receipt.js +560 -118
  93. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
  94. package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
  95. package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
  96. package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
  97. package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
  98. package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
  99. package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
  100. package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
  101. package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
  102. package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
  103. package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
  104. package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
  105. package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
  106. package/dist/core/codex-lb/provider-credentials/types.js +1 -0
  107. package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
  108. package/dist/core/codex-lb/provider-credentials.js +5 -0
  109. package/dist/core/codex-lb/provider-registry.js +323 -0
  110. package/dist/core/codex-lb/provider-route-policy.js +118 -0
  111. package/dist/core/codex-lb/request-route-resolver.js +172 -0
  112. package/dist/core/codex-lb/route-index.js +148 -0
  113. package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
  114. package/dist/core/commands/basic-cli.js +1 -1
  115. package/dist/core/commands/check-command.js +3 -10
  116. package/dist/core/commands/gate-result-contract.js +10 -14
  117. package/dist/core/commands/intent-normalization/normalizer.js +1 -1
  118. package/dist/core/commands/mad-sks-command.js +2 -6
  119. package/dist/core/commands/release-command.js +11 -1
  120. package/dist/core/commands/telegram-command.js +46 -6
  121. package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
  122. package/dist/core/doctor/skill-legacy-surface.js +5 -5
  123. package/dist/core/evidence/v2/evidence-key.js +30 -3
  124. package/dist/core/feature-fixtures.js +8 -1
  125. package/dist/core/feature-registry.js +3 -2
  126. package/dist/core/hooks-runtime/stop-finalization.js +11 -1
  127. package/dist/core/hooks-runtime/subagent-context.js +15 -1
  128. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
  129. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  130. package/dist/core/hooks-runtime.js +73 -87
  131. package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
  132. package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
  133. package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
  134. package/dist/core/imagegen/imagegen-capability.js +43 -151
  135. package/dist/core/init/skills/filesystem.js +89 -0
  136. package/dist/core/init/skills/inventory.js +97 -0
  137. package/dist/core/init/skills/manifest.js +151 -0
  138. package/dist/core/init/skills.js +5 -325
  139. package/dist/core/init.js +0 -63
  140. package/dist/core/install/installed-package-smoke.js +1 -1
  141. package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
  142. package/dist/core/policy/latest-version-guidance.js +15 -2
  143. package/dist/core/preflight/parallel-preflight-engine.js +27 -51
  144. package/dist/core/provider/provider-badge.js +9 -4
  145. package/dist/core/provider/provider-context.js +57 -107
  146. package/dist/core/providers/openrouter/openrouter-account.js +3 -1
  147. package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
  148. package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
  149. package/dist/core/release/gate-manifest.js +0 -1
  150. package/dist/core/release/main-push-guard.js +13 -0
  151. package/dist/core/release/main-push-receipt.js +11 -0
  152. package/dist/core/release/physical-release-gates.js +664 -61
  153. package/dist/core/release/publish-preflight.js +3 -3
  154. package/dist/core/release/release-gate-batch-runner.js +19 -6
  155. package/dist/core/release/release-gate-dag.js +33 -6
  156. package/dist/core/release/release-gate-node.js +1 -3
  157. package/dist/core/release/release-gate-output-contract.js +55 -0
  158. package/dist/core/release/release-pack-contract.js +1 -1
  159. package/dist/core/release/release-pack-receipt.js +209 -8
  160. package/dist/core/release/release-real-contract.js +1 -1
  161. package/dist/core/release/stage-publish.js +106 -28
  162. package/dist/core/retention/temp-lease.js +204 -0
  163. package/dist/core/retention.js +18 -195
  164. package/dist/core/routes.js +1 -2
  165. package/dist/core/safety/intent-contract/intent-contract.js +3 -3
  166. package/dist/core/subagents/naruto-host-credentials.js +6 -1
  167. package/dist/core/subagents/official-subagent-config.js +27 -9
  168. package/dist/core/subagents/official-subagent-runner.js +14 -31
  169. package/dist/core/subagents/wave-lifecycle.js +36 -8
  170. package/dist/core/subagents/wave-parent-guidance.js +2 -1
  171. package/dist/core/telegram/doctor.js +2 -2
  172. package/dist/core/telegram/liveness.js +10 -4
  173. package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
  174. package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
  175. package/dist/core/update/update-migration-state/session-state-split.js +247 -0
  176. package/dist/core/update/update-migration-state/simple-stages.js +46 -0
  177. package/dist/core/update/update-migration-state.js +4 -481
  178. package/dist/core/version.js +1 -1
  179. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
  180. package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
  181. package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
  182. package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
  183. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
  184. package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
  185. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
  186. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
  187. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
  188. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
  189. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  190. package/dist/scripts/canonical-test-runner.js +7 -1
  191. package/dist/scripts/check-runtime-schemas.js +7 -2
  192. package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
  193. package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
  194. package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
  195. package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
  196. package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
  197. package/dist/scripts/desktop-bridge-transport-client.js +116 -0
  198. package/dist/scripts/desktop-bridge-unification-check.js +276 -0
  199. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
  200. package/dist/scripts/gate-policy-audit-check.js +52 -2
  201. package/dist/scripts/provider-badge-context-check.js +55 -12
  202. package/dist/scripts/publish-preflight.js +1 -2
  203. package/dist/scripts/release-affected-selector-check.js +1 -2
  204. package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
  205. package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
  206. package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
  207. package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
  208. package/dist/scripts/release-gate-batch-runner-check.js +9 -0
  209. package/dist/scripts/release-gate-dag-runner-check.js +6 -0
  210. package/dist/scripts/release-gate-dag-runner.js +4 -2
  211. package/dist/scripts/release-metadata-check.js +16 -3
  212. package/dist/scripts/release-pack-receipt.js +29 -6
  213. package/dist/scripts/release-physical-gates-check.js +39 -0
  214. package/dist/scripts/release-real-check.js +7 -5
  215. package/package.json +6 -8
  216. package/release-gates.v2.json +156 -157
  217. package/runtime-required-scripts.json +11 -11
  218. package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
  219. package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
  220. package/schemas/release/release-gate-node.schema.json +2 -3
  221. package/dist/cli/codex-lb-setup-warning-output.js +0 -17
  222. package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
  223. package/dist/commands/codex-lb.js +0 -1232
  224. package/dist/core/architecture-hardening/migration/migration.js +0 -111
  225. package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
  226. package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
  227. package/dist/core/codex-app/child-policy/child-policy.js +0 -35
  228. package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
  229. package/dist/core/codex-app/glm-model-profile.js +0 -1
  230. package/dist/core/codex-app/glm-profile-installer.js +0 -191
  231. package/dist/core/codex-app/glm-profile-schema.js +0 -42
  232. package/dist/core/codex-app/multi-provider-router.js +0 -391
  233. package/dist/core/codex-app/openrouter-activate.js +0 -405
  234. package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
  235. package/dist/core/codex-app/provider-child-policy.js +0 -64
  236. package/dist/core/codex-app/provider-mode.js +0 -91
  237. package/dist/core/codex-app/provider-session-policy.js +0 -85
  238. package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
  239. package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
  240. package/dist/core/codex-lb/codex-lb-setup.js +0 -293
  241. package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
  242. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
  243. package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
  244. package/dist/core/codex-lb/desktop-mode.js +0 -32
  245. package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
  246. package/dist/core/codex-lb/legacy-migration.js +0 -576
  247. package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
  248. package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
  249. package/dist/core/codex-lb/routing-truth.js +0 -327
  250. package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
  251. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
  252. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
  253. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
  254. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
  255. package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
  256. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
  257. package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
  258. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
  259. package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
  260. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
  261. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
@@ -1,405 +0,0 @@
1
- import path from 'node:path';
2
- import os from 'node:os';
3
- import { ensureDir, exists, readText, nowIso } from '../fsx.js';
4
- import { openRouterSecretPaths, resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
5
- import { OPENROUTER_AUTH_COMMAND, OPENROUTER_AUTH_REFRESH_INTERVAL_MS, OPENROUTER_AUTH_TIMEOUT_MS, openRouterAuthCommandArgs, OPENROUTER_PROVIDER_ID, normalizeOpenRouterModelId } from './openrouter-provider.js';
6
- import { installCodexAppGlmProfile } from './glm-profile-installer.js';
7
- import { restartCodexApp } from './codex-app-restart.js';
8
- import { codexLbConfigPath, ensureGlobalCodexAppGlmProfile, unselectCodexLbProvider } from '../../cli/install-helpers.js';
9
- import { ensureTrailingNewline, normalizeCodexFastModeUiConfig, safeWriteCodexConfigToml, upsertTopLevelTomlString } from '../codex-runtime/codex-desktop-config-policy.js';
10
- import { readTopLevelTomlString, sksOpenRouterCatalogPath } from './codex-model-catalog.js';
11
- import { resolveCatalogPath } from './multi-provider-router-support.js';
12
- import { openRouterCatalogBindDecision, writeOpenRouterManagedCatalog } from './openrouter-model-catalog.js';
13
- import { classifyCodexDesktopRouting } from './codex-desktop-routing-ownership.js';
14
- import { desktopPickerStatusFromCache, invalidateCodexModelsCache } from './codex-models-cache.js';
15
- import { assessThreadVisibilityImpact, captureDesktopRoutingSnapshot, desktopRoutingSnapshotPath, readDesktopRoutingSnapshot, remapThreadCatalogProvider, restoreDesktopRoutingSnapshot, writeDesktopRoutingSnapshot } from './desktop-routing-snapshot.js';
16
- import { escapeRegExp } from '../text/regex.js';
17
- export async function openRouterStatus(input = {}) {
18
- const home = input.home || process.env.HOME || os.homedir();
19
- const configPath = input.configPath || codexLbConfigPath(home);
20
- const config = await readText(configPath, '');
21
- const env = { ...(input.env || process.env), HOME: home };
22
- const key = await resolveOpenRouterApiKey({ env });
23
- const authArgs = openRouterAuthCommandArgs(openRouterSecretPaths(env).keyPath);
24
- const providerPresent = new RegExp(`\\[model_providers\\.${OPENROUTER_PROVIDER_ID}\\]`).test(config);
25
- const providerBody = tomlTableBody(config, `model_providers.${OPENROUTER_PROVIDER_ID}`);
26
- const providerEnvKeyPresent = hasTomlKey(providerBody, 'env_key');
27
- const authBody = tomlTableBody(config, `model_providers.${OPENROUTER_PROVIDER_ID}.auth`);
28
- const providerAuthPresent = Boolean(authBody);
29
- const providerAuthConflict = providerEnvKeyPresent && providerAuthPresent;
30
- const providerAuthValid = providerAuthPresent
31
- && !providerAuthConflict
32
- && hasTomlString(authBody, 'command', OPENROUTER_AUTH_COMMAND)
33
- && hasTomlStringArray(authBody, 'args', authArgs)
34
- && hasTomlInteger(authBody, 'timeout_ms', OPENROUTER_AUTH_TIMEOUT_MS)
35
- && hasTomlInteger(authBody, 'refresh_interval_ms', OPENROUTER_AUTH_REFRESH_INTERVAL_MS);
36
- const selected = readTopLevelTomlString(config, 'model_provider') === OPENROUTER_PROVIDER_ID;
37
- const model = readTopLevelTomlString(config, 'model');
38
- const managedCatalogPath = sksOpenRouterCatalogPath({ home, env });
39
- const configuredCatalogPath = readTopLevelTomlString(config, 'model_catalog_json');
40
- const modelCatalogBound = Boolean(configuredCatalogPath
41
- && resolveCatalogPath(configuredCatalogPath, { home, env, configPath }) === path.resolve(managedCatalogPath));
42
- const blockers = [];
43
- if (!key.key)
44
- blockers.push('openrouter_key_missing');
45
- if (!providerPresent)
46
- blockers.push('openrouter_provider_missing');
47
- if (providerAuthConflict)
48
- blockers.push('openrouter_provider_auth_env_key_conflict');
49
- else if (!providerAuthPresent)
50
- blockers.push('openrouter_provider_auth_missing');
51
- else if (!providerAuthValid)
52
- blockers.push('openrouter_provider_auth_invalid');
53
- if (selected && !model)
54
- blockers.push('openrouter_model_missing');
55
- const ownership = classifyCodexDesktopRouting(config, { home, env, configPath });
56
- const currentProvider = readTopLevelTomlString(config, 'model_provider');
57
- const threadVisibility = assessThreadVisibilityImpact({
58
- home,
59
- env,
60
- currentProvider,
61
- targetProvider: selected ? OPENROUTER_PROVIDER_ID : (currentProvider || 'openai')
62
- });
63
- const previousSnapshot = await readDesktopRoutingSnapshot({ home, env });
64
- const previousRoutingRestoreAvailable = Boolean(previousSnapshot && previousSnapshot.model_provider !== OPENROUTER_PROVIDER_ID);
65
- const warnings = [
66
- ...(selected && !modelCatalogBound ? ['openrouter_model_catalog_not_bound'] : []),
67
- ...ownership.warnings,
68
- ...(selected && threadVisibility.hidden_if_switched > 0
69
- ? ['desktop_other_provider_threads_hidden_until_restore']
70
- : []),
71
- ...(previousRoutingRestoreAvailable ? ['desktop_routing_snapshot_restore_available'] : [])
72
- ];
73
- return {
74
- schema: 'sks.codex-app-openrouter-status.v1',
75
- ok: blockers.length === 0,
76
- key_present: Boolean(key.key),
77
- key_source: key.source || null,
78
- provider_present: providerPresent,
79
- provider_env_key_present: providerEnvKeyPresent,
80
- provider_auth_present: providerAuthPresent,
81
- provider_auth_conflict: providerAuthConflict,
82
- provider_auth_valid: providerAuthValid,
83
- selected,
84
- model,
85
- model_source: model ? 'config' : null,
86
- model_catalog_path: managedCatalogPath,
87
- model_catalog_bound: modelCatalogBound,
88
- config_path: configPath,
89
- routing_ownership: ownership,
90
- desktop_picker: desktopPickerStatusFromCache({
91
- catalogOk: modelCatalogBound,
92
- cache: null,
93
- restartAppRequested: false
94
- }),
95
- thread_visibility: threadVisibility,
96
- previous_routing_restore_available: previousRoutingRestoreAvailable,
97
- blockers,
98
- warnings
99
- };
100
- }
101
- export async function useOpenRouter(input) {
102
- const model = normalizeOpenRouterModelId(input.model);
103
- if (!model) {
104
- return {
105
- schema: 'sks.codex-app-use-openrouter.v1',
106
- generated_at: nowIso(),
107
- ok: false,
108
- status: 'blocked',
109
- mode: 'openrouter',
110
- blockers: ['openrouter_model_invalid'],
111
- warnings: [],
112
- hint: 'Choose a catalog model or pass --model <openrouter-model-id>.'
113
- };
114
- }
115
- const home = input.home || process.env.HOME || os.homedir();
116
- const configPath = input.configPath || codexLbConfigPath(home);
117
- const env = { ...(input.env || process.env), HOME: home };
118
- await ensureDir(path.dirname(configPath));
119
- const key = await resolveOpenRouterApiKey({ env });
120
- if (!key.key) {
121
- return {
122
- schema: 'sks.codex-app-use-openrouter.v1',
123
- generated_at: nowIso(),
124
- ok: false,
125
- status: 'blocked',
126
- mode: 'openrouter',
127
- model,
128
- blockers: ['openrouter_key_missing'],
129
- warnings: [],
130
- hint: 'Save a key first: sks codex-app set-openrouter-key --api-key-stdin'
131
- };
132
- }
133
- const profile = await installCodexAppGlmProfile({
134
- root: input.root,
135
- apply: true,
136
- home,
137
- env,
138
- configPath
139
- });
140
- const activationProfile = {
141
- ...profile,
142
- profile: {
143
- ...profile.profile,
144
- model
145
- }
146
- };
147
- if (!profile.ok) {
148
- return {
149
- schema: 'sks.codex-app-use-openrouter.v1',
150
- generated_at: nowIso(),
151
- ok: false,
152
- status: 'blocked',
153
- mode: 'openrouter',
154
- model,
155
- profile: activationProfile,
156
- blockers: profile.blockers,
157
- warnings: profile.warnings || []
158
- };
159
- }
160
- const configExistedBefore = await exists(configPath);
161
- const currentBeforeSwitch = await readText(configPath, '');
162
- const previousProvider = readTopLevelTomlString(currentBeforeSwitch, 'model_provider');
163
- const threadVisibility = assessThreadVisibilityImpact({
164
- home,
165
- env,
166
- currentProvider: previousProvider,
167
- targetProvider: OPENROUTER_PROVIDER_ID
168
- });
169
- const preserveThreadSidebar = input.preserveThreadSidebar !== false;
170
- const routingSnapshot = captureDesktopRoutingSnapshot(currentBeforeSwitch, {
171
- reason: 'use-openrouter'
172
- });
173
- let snapshotWrite;
174
- if (routingSnapshot.model_provider === OPENROUTER_PROVIDER_ID) {
175
- const existingSnapshot = await readDesktopRoutingSnapshot({ home, env });
176
- const crossProviderSnapshotPreserved = Boolean(existingSnapshot && existingSnapshot.model_provider !== OPENROUTER_PROVIDER_ID);
177
- snapshotWrite = {
178
- ok: crossProviderSnapshotPreserved,
179
- path: desktopRoutingSnapshotPath({ home, env }),
180
- skipped: crossProviderSnapshotPreserved
181
- ? 'preserved_existing_cross_provider_snapshot'
182
- : 'self_referential_snapshot_not_written'
183
- };
184
- }
185
- else {
186
- snapshotWrite = await writeDesktopRoutingSnapshot(routingSnapshot, { home, env });
187
- }
188
- const unselect = await unselectCodexLbProvider({
189
- home,
190
- configPath,
191
- processEnv: env
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
- }
218
- const modelCatalog = await writeOpenRouterManagedCatalog({ model, home, env });
219
- const current = await readText(configPath, '');
220
- const catalogBind = openRouterCatalogBindDecision(current, { home, env, configPath });
221
- let next = upsertTopLevelTomlString(current, 'model_provider', OPENROUTER_PROVIDER_ID);
222
- next = upsertTopLevelTomlString(next, 'model', model);
223
- if (modelCatalog.ok && catalogBind.bindable) {
224
- next = upsertTopLevelTomlString(next, 'model_catalog_json', modelCatalog.path);
225
- }
226
- next = normalizeCodexFastModeUiConfig(next);
227
- next = ensureTrailingNewline(next);
228
- const write = await safeWriteCodexConfigToml(configPath, current, next, 'openrouter-use', {
229
- verifyUnchangedBeforeWrite: true,
230
- expectedBeforeExists: configExistedBefore
231
- });
232
- if (!write.ok) {
233
- return {
234
- schema: 'sks.codex-app-use-openrouter.v1',
235
- generated_at: nowIso(),
236
- ok: false,
237
- status: 'blocked',
238
- mode: 'openrouter',
239
- model,
240
- write,
241
- unselect,
242
- routing_snapshot: routingSnapshot,
243
- routing_snapshot_write: snapshotWrite,
244
- thread_visibility: threadVisibility,
245
- thread_sidebar_remap: null,
246
- blockers: [String(write.status || 'openrouter_config_write_blocked')],
247
- warnings: []
248
- };
249
- }
250
- let threadSidebarRemap = null;
251
- let threadSidebarMeta;
252
- if (preserveThreadSidebar
253
- && previousProvider
254
- && previousProvider !== OPENROUTER_PROVIDER_ID) {
255
- threadSidebarRemap = remapThreadCatalogProvider({
256
- home,
257
- env,
258
- fromProvider: previousProvider,
259
- toProvider: OPENROUTER_PROVIDER_ID
260
- });
261
- if (threadSidebarRemap.ok && threadSidebarRemap.remapped > 0) {
262
- threadSidebarMeta = {
263
- remapped: true,
264
- from_provider: previousProvider,
265
- to_provider: OPENROUTER_PROVIDER_ID,
266
- thread_ids: threadSidebarRemap.thread_ids,
267
- catalog_db: threadSidebarRemap.catalog_db
268
- };
269
- await writeDesktopRoutingSnapshot(captureDesktopRoutingSnapshot(currentBeforeSwitch, {
270
- reason: 'use-openrouter',
271
- threadSidebar: threadSidebarMeta
272
- }), { home, env });
273
- }
274
- }
275
- const restart = await (input.restartImpl || restartCodexApp)({ enabled: Boolean(input.restartApp) });
276
- const cache = modelCatalog?.models_cache
277
- || await invalidateCodexModelsCache({
278
- home,
279
- env,
280
- catalogPath: modelCatalog.ok ? modelCatalog.path : null,
281
- seedMode: 'merge'
282
- });
283
- const status = await openRouterStatus({ home, configPath, env });
284
- const modelCatalogApplied = Boolean(modelCatalog.ok
285
- && (!catalogBind.bindable || status.model_catalog_bound));
286
- const configApplied = Boolean(status.selected
287
- && status.key_present
288
- && status.provider_present
289
- && status.provider_auth_valid
290
- && status.model === model
291
- && modelCatalogApplied);
292
- const desktopPicker = desktopPickerStatusFromCache({
293
- catalogOk: modelCatalogApplied,
294
- cache,
295
- restartAppRequested: Boolean(input.restartApp)
296
- });
297
- return {
298
- schema: 'sks.codex-app-use-openrouter.v1',
299
- generated_at: nowIso(),
300
- ok: configApplied,
301
- status: configApplied ? (restart.ok ? 'active' : 'active_restart_blocked') : 'activation_incomplete',
302
- mode: 'openrouter',
303
- model,
304
- profile: activationProfile,
305
- unselect,
306
- write,
307
- restart_app: restart,
308
- config_applied: configApplied,
309
- restart_ok: restart.ok,
310
- openrouter: {
311
- ...status,
312
- desktop_picker: desktopPicker
313
- },
314
- model_catalog: modelCatalog,
315
- model_catalog_bind: catalogBind,
316
- models_cache: cache,
317
- desktop_picker: desktopPicker,
318
- routing_ownership: status.routing_ownership,
319
- routing_snapshot: routingSnapshot,
320
- routing_snapshot_write: snapshotWrite,
321
- previous_routing_restore_available: Boolean(snapshotWrite.ok),
322
- thread_visibility: threadVisibility,
323
- thread_sidebar_remap: threadSidebarRemap,
324
- readiness: {
325
- selected: status.selected,
326
- key_present: status.key_present,
327
- provider_present: status.provider_present,
328
- provider_auth_present: status.provider_auth_present,
329
- provider_auth_valid: status.provider_auth_valid,
330
- model: status.model,
331
- model_catalog_ok: modelCatalog.ok,
332
- model_catalog_bound: status.model_catalog_bound,
333
- features_normalized: true,
334
- config_applied: configApplied,
335
- restart_ok: restart.ok,
336
- models_cache_invalidated: desktopPicker.models_cache_invalidated,
337
- ok: configApplied
338
- },
339
- blockers: [
340
- ...(status.selected ? [] : ['openrouter_not_selected']),
341
- ...(status.model === model ? [] : ['openrouter_model_not_applied']),
342
- ...(status.provider_auth_valid ? [] : ['openrouter_provider_auth_not_applied']),
343
- ...(modelCatalogApplied
344
- ? []
345
- : (modelCatalog.blockers.length ? modelCatalog.blockers : ['openrouter_model_catalog_not_applied']))
346
- ],
347
- warnings: [
348
- ...(unselect?.ok === false ? [`codex_lb_unselect:${unselect.provider_error || unselect.status}`] : []),
349
- ...(restart.ok ? [] : (restart.blockers || ['openrouter_restart_blocked']).map((blocker) => `restart:${blocker}`)),
350
- ...(profile.warnings || []),
351
- ...(catalogBind.bindable ? [] : ['openrouter_model_catalog_user_catalog_preserved']),
352
- ...modelCatalog.warnings,
353
- ...cache.warnings,
354
- ...(desktopPicker.models_cache_invalidated ? [] : ['openrouter_models_cache_not_invalidated']),
355
- ...(threadSidebarRemap && !threadSidebarRemap.ok
356
- ? [`thread_sidebar_remap_failed:${threadSidebarRemap.error || 'unknown'}`]
357
- : []),
358
- ...(threadVisibility.hidden_if_switched > 0 && !(threadSidebarMeta?.remapped)
359
- ? [`desktop_hides_${threadVisibility.hidden_if_switched}_other_provider_threads`]
360
- : []),
361
- ...(snapshotWrite.ok
362
- ? []
363
- : [snapshotWrite.skipped
364
- ? `routing_snapshot_not_written:${snapshotWrite.skipped}`
365
- : `routing_snapshot_write_failed:${snapshotWrite.error || 'unknown'}`]),
366
- 'desktop_picker_restart_recommended_after_catalog_change',
367
- 'restore_previous_provider_via_sks_codex_app_restore_desktop_routing'
368
- ],
369
- hint: snapshotWrite.ok
370
- ? 'Other-provider Desktop chats stay visible when sidebar remap succeeds; otherwise restore with: sks codex-app restore-desktop-routing --restart-app'
371
- : 'If Desktop chats disappeared, run: sks codex-app restore-desktop-routing --restart-app'
372
- };
373
- }
374
- export { restoreDesktopRoutingSnapshot };
375
- export async function ensureOpenRouterProviderInstalled(opts = {}) {
376
- return ensureGlobalCodexAppGlmProfile(opts);
377
- }
378
- function tomlTableBody(text, table) {
379
- const header = `[${table}]`;
380
- const lines = String(text || '').split('\n');
381
- const start = lines.findIndex((line) => line.trim() === header);
382
- if (start === -1)
383
- return '';
384
- const body = [];
385
- for (let index = start + 1; index < lines.length; index += 1) {
386
- const line = lines[index];
387
- if (/^\s*\[[^\]]+\]\s*$/.test(line || ''))
388
- break;
389
- body.push(line || '');
390
- }
391
- return body.join('\n');
392
- }
393
- function hasTomlString(text, key, value) {
394
- return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?$`, 'm').test(text);
395
- }
396
- function hasTomlKey(text, key) {
397
- return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`, 'm').test(text);
398
- }
399
- function hasTomlStringArray(text, key, values) {
400
- const expected = values.map((value) => JSON.stringify(value)).join(', ');
401
- return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*\\[${escapeRegExp(expected)}\\]\\s*(?:#.*)?$`, 'm').test(text);
402
- }
403
- function hasTomlInteger(text, key, value) {
404
- return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*${value}\\s*(?:#.*)?$`, 'm').test(text);
405
- }
@@ -1,217 +0,0 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
- import { nowIso, readText, writeTextAtomic } from '../fsx.js';
4
- import { CODEX_MODEL_CATALOG_MAX_MODELS, catalogModelRowBlockers, normalizeCodexModelId, readCodexModelCatalogFile, readTopLevelTomlString, sksOpenRouterCatalogPath } from './codex-model-catalog.js';
5
- import { isSksManagedCatalogPath, resolveCatalogPath } from './multi-provider-router-support.js';
6
- import { OPENROUTER_SELECTABLE_REASONING_EFFORTS } from './openrouter-provider.js';
7
- import { OPENROUTER_CATALOG_BASE_INSTRUCTIONS } from './openrouter-base-instructions.js';
8
- import { ensureTrailingNewline, removeTopLevelTomlKeyIfValue, safeWriteCodexConfigToml, upsertTopLevelTomlString } from '../codex-runtime/codex-desktop-config-policy.js';
9
- import { readRoleModelPreferences } from '../subagents/role-model-preferences.js';
10
- import { stampRoleModelCatalogPriorities } from './role-model-catalog-priority.js';
11
- import { invalidateCodexModelsCache } from './codex-models-cache.js';
12
- const OPENROUTER_REASONING_LEVEL_DESCRIPTIONS = {
13
- none: 'No extra reasoning pass',
14
- minimal: 'Fastest responses with minimal reasoning',
15
- low: 'Fast responses with lighter reasoning',
16
- medium: 'Balances speed and reasoning depth for everyday tasks',
17
- high: 'Greater reasoning depth for complex problems',
18
- xhigh: 'Extra high reasoning depth for complex problems'
19
- };
20
- export function openRouterCatalogModelRow(model) {
21
- return {
22
- slug: model,
23
- display_name: model,
24
- description: 'OpenRouter model activated by SKS',
25
- default_reasoning_level: 'medium',
26
- supported_reasoning_levels: OPENROUTER_SELECTABLE_REASONING_EFFORTS.map((effort) => ({
27
- effort,
28
- description: OPENROUTER_REASONING_LEVEL_DESCRIPTIONS[effort] || `${effort} reasoning`
29
- })),
30
- shell_type: 'default',
31
- visibility: 'list',
32
- supported_in_api: true,
33
- priority: 0,
34
- base_instructions: OPENROUTER_CATALOG_BASE_INSTRUCTIONS,
35
- default_reasoning_summary: 'auto',
36
- supports_reasoning_summaries: true,
37
- support_verbosity: false,
38
- truncation_policy: { mode: 'bytes', limit: 10_000 },
39
- supports_parallel_tool_calls: true,
40
- experimental_supported_tools: [],
41
- input_modalities: ['text'],
42
- context_window: 272_000,
43
- max_context_window: 272_000,
44
- multi_agent_version: 'v2'
45
- };
46
- }
47
- export async function writeOpenRouterManagedCatalog(input) {
48
- const env = input.env || process.env;
49
- const home = input.home || env.HOME || os.homedir();
50
- const catalogPath = sksOpenRouterCatalogPath({ home, env });
51
- const model = normalizeCodexModelId(input.model);
52
- if (!model) {
53
- return {
54
- schema: 'sks.openrouter-model-catalog-write.v1',
55
- ok: false,
56
- status: 'failed',
57
- path: catalogPath,
58
- models: [],
59
- catalog: null,
60
- blockers: ['openrouter_model_invalid'],
61
- warnings: []
62
- };
63
- }
64
- const warnings = [];
65
- const existingRows = [];
66
- const seenSlugs = new Set([model]);
67
- const existingText = await readText(catalogPath, '');
68
- if (existingText.trim()) {
69
- try {
70
- const parsed = JSON.parse(existingText);
71
- if (parsed && typeof parsed === 'object' && Array.isArray(parsed.models)) {
72
- for (const row of parsed.models) {
73
- if (!row || typeof row !== 'object' || Array.isArray(row))
74
- continue;
75
- const slug = normalizeCodexModelId(row.slug);
76
- if (!slug || seenSlugs.has(slug))
77
- continue;
78
- if (catalogModelRowBlockers(row).length) {
79
- warnings.push(`openrouter_model_catalog_previous_row_dropped:${slug}`);
80
- continue;
81
- }
82
- seenSlugs.add(slug);
83
- existingRows.push(row);
84
- }
85
- }
86
- else {
87
- warnings.push('openrouter_model_catalog_previous_shape_invalid');
88
- }
89
- }
90
- catch {
91
- warnings.push('openrouter_model_catalog_previous_invalid_json');
92
- }
93
- }
94
- const baseRows = [openRouterCatalogModelRow(model), ...existingRows].slice(0, CODEX_MODEL_CATALOG_MAX_MODELS);
95
- const rolePrefs = await readRoleModelPreferences({ env }).catch(() => null);
96
- const stamped = stampRoleModelCatalogPriorities(baseRows, rolePrefs?.store || null);
97
- if (stamped.missing_from_catalog.length) {
98
- warnings.push(...stamped.missing_from_catalog.map((slug) => `role_model_not_in_openrouter_catalog:${slug}`));
99
- }
100
- const rows = stamped.rows;
101
- const nextText = `${JSON.stringify({ generated_at: nowIso(), generated_by: 'sks-openrouter-activation', models: rows }, null, 2)}\n`;
102
- try {
103
- await writeTextAtomic(catalogPath, nextText, { mode: 0o600 });
104
- }
105
- catch (err) {
106
- return {
107
- schema: 'sks.openrouter-model-catalog-write.v1',
108
- ok: false,
109
- status: 'failed',
110
- path: catalogPath,
111
- models: rows.map((row) => String(row.slug)),
112
- catalog: null,
113
- blockers: [`openrouter_model_catalog_write_failed:${err?.code || err?.message || 'unknown'}`],
114
- warnings
115
- };
116
- }
117
- const cache = await invalidateCodexModelsCache({ home, env, catalogPath, seedMode: 'merge' });
118
- if (!cache.ok)
119
- warnings.push(...cache.blockers);
120
- else
121
- warnings.push(...cache.warnings);
122
- const catalog = await readCodexModelCatalogFile({ filePath: catalogPath, configured: true });
123
- return {
124
- schema: 'sks.openrouter-model-catalog-write.v1',
125
- ok: catalog.ok && catalog.models.some((entry) => entry.model === model),
126
- status: existingText.trim() === nextText.trim() ? 'current' : 'written',
127
- path: catalogPath,
128
- models: catalog.models.map((entry) => entry.model),
129
- catalog,
130
- models_cache: cache,
131
- role_model_priority: {
132
- stamped: stamped.stamped,
133
- stamped_models: stamped.stamped_models,
134
- missing_from_catalog: stamped.missing_from_catalog
135
- },
136
- blockers: catalog.blockers,
137
- warnings
138
- };
139
- }
140
- export function openRouterCatalogBindDecision(configText, input = {}) {
141
- const env = input.env || process.env;
142
- const home = input.home || env.HOME || os.homedir();
143
- const catalogPath = sksOpenRouterCatalogPath({ home, env });
144
- const configured = readTopLevelTomlString(String(configText || ''), 'model_catalog_json');
145
- if (!configured)
146
- return { bindable: true, bound: false, configured_path: null, reason: 'unconfigured' };
147
- const resolved = resolveCatalogPath(configured, {
148
- home,
149
- env,
150
- ...(input.configPath === undefined ? {} : { configPath: input.configPath })
151
- });
152
- if (resolved === path.resolve(catalogPath)) {
153
- return { bindable: true, bound: true, configured_path: configured, reason: 'already_bound' };
154
- }
155
- if (isSksManagedCatalogPath(resolved, { home, env })) {
156
- return { bindable: true, bound: false, configured_path: configured, reason: 'sks_managed_replaceable' };
157
- }
158
- return { bindable: false, bound: false, configured_path: configured, reason: 'user_catalog_preserved' };
159
- }
160
- export async function ensureOpenRouterModelCatalog(input) {
161
- const env = input.env || process.env;
162
- const home = input.home || env.HOME || os.homedir();
163
- const current = await readText(input.configPath, '');
164
- const selectedProvider = readTopLevelTomlString(current, 'model_provider');
165
- if (selectedProvider !== 'openrouter') {
166
- const bind = openRouterCatalogBindDecision(current, { home, env, configPath: input.configPath });
167
- if (bind.bound) {
168
- const configured = readTopLevelTomlString(current, 'model_catalog_json') || '';
169
- const next = ensureTrailingNewline(removeTopLevelTomlKeyIfValue(current, 'model_catalog_json', configured));
170
- const safeWrite = await safeWriteCodexConfigToml(input.configPath, current, next, 'openrouter-model-catalog-unbind');
171
- return {
172
- schema: 'sks.openrouter-model-catalog-repair.v1',
173
- ok: safeWrite.ok,
174
- status: safeWrite.ok ? 'unbound_dangling' : String(safeWrite.status || 'config_write_blocked'),
175
- selected_provider: selectedProvider,
176
- write: safeWrite,
177
- blockers: safeWrite.ok ? [] : [String(safeWrite.status || 'openrouter_model_catalog_unbind_failed')],
178
- warnings: []
179
- };
180
- }
181
- return { schema: 'sks.openrouter-model-catalog-repair.v1', ok: true, status: 'skipped', reason: 'openrouter_not_selected' };
182
- }
183
- const model = normalizeCodexModelId(readTopLevelTomlString(current, 'model'));
184
- if (!model) {
185
- return { schema: 'sks.openrouter-model-catalog-repair.v1', ok: false, status: 'blocked', blockers: ['openrouter_model_missing'] };
186
- }
187
- const write = await writeOpenRouterManagedCatalog({ model, home, env });
188
- const bind = openRouterCatalogBindDecision(current, { home, env, configPath: input.configPath });
189
- if (!write.ok || !bind.bindable || bind.bound) {
190
- return {
191
- schema: 'sks.openrouter-model-catalog-repair.v1',
192
- ok: write.ok,
193
- status: write.ok ? (bind.bound ? 'current' : 'catalog_written_not_bound') : 'failed',
194
- model,
195
- catalog: write,
196
- bind,
197
- blockers: write.blockers,
198
- warnings: [
199
- ...write.warnings,
200
- ...(bind.bindable || bind.bound ? [] : ['openrouter_model_catalog_user_catalog_preserved'])
201
- ]
202
- };
203
- }
204
- const next = ensureTrailingNewline(upsertTopLevelTomlString(current, 'model_catalog_json', write.path));
205
- const safeWrite = await safeWriteCodexConfigToml(input.configPath, current, next, 'openrouter-model-catalog');
206
- return {
207
- schema: 'sks.openrouter-model-catalog-repair.v1',
208
- ok: safeWrite.ok,
209
- status: safeWrite.ok ? 'bound' : String(safeWrite.status || 'config_write_blocked'),
210
- model,
211
- catalog: write,
212
- bind,
213
- write: safeWrite,
214
- blockers: safeWrite.ok ? [] : [String(safeWrite.status || 'openrouter_model_catalog_bind_failed')],
215
- warnings: write.warnings
216
- };
217
- }
@@ -1,64 +0,0 @@
1
- import { assertProviderModeModel } from './provider-mode.js';
2
- export function decideProviderChildModel(input) {
3
- const mode = input.session.mode;
4
- if (mode === 'chatgpt-oauth') {
5
- if (input.requestedModel) {
6
- return blocked('native-oauth', 'oauth_child_model_override_forbidden');
7
- }
8
- return {
9
- schema: 'sks.provider-child-model-decision.v1',
10
- ok: true,
11
- strategy: 'native-oauth',
12
- model_override: null,
13
- inherit_session_mode: true,
14
- inherit_credential_binding: true,
15
- settings_owner: 'codex-native',
16
- blockers: []
17
- };
18
- }
19
- const requested = String(input.requestedModel || '').trim();
20
- if (!requested)
21
- return blocked(mode === 'codex-lb' ? 'codex-lb-dynamic' : 'openrouter-registered', 'child_model_selection_required');
22
- if (mode === 'codex-lb') {
23
- try {
24
- assertProviderModeModel('codex-lb', requested, input.session.allowed_models);
25
- }
26
- catch (error) {
27
- return blocked('codex-lb-dynamic', `child_${error.message}`);
28
- }
29
- return allowed('codex-lb-dynamic', requested, 'codex-lb-session');
30
- }
31
- const registered = input.registeredOpenRouterModels || [];
32
- try {
33
- assertProviderModeModel('openrouter', requested, registered);
34
- assertProviderModeModel('openrouter', requested, input.session.allowed_models);
35
- }
36
- catch (error) {
37
- return blocked('openrouter-registered', `child_${error.message}`);
38
- }
39
- return allowed('openrouter-registered', requested, 'sks-center');
40
- }
41
- function allowed(strategy, model, owner) {
42
- return {
43
- schema: 'sks.provider-child-model-decision.v1',
44
- ok: true,
45
- strategy,
46
- model_override: model,
47
- inherit_session_mode: true,
48
- inherit_credential_binding: true,
49
- settings_owner: owner,
50
- blockers: []
51
- };
52
- }
53
- function blocked(strategy, blocker) {
54
- return {
55
- schema: 'sks.provider-child-model-decision.v1',
56
- ok: false,
57
- strategy,
58
- model_override: null,
59
- inherit_session_mode: true,
60
- inherit_credential_binding: true,
61
- settings_owner: strategy === 'native-oauth' ? 'codex-native' : strategy === 'openrouter-registered' ? 'sks-center' : 'codex-lb-session',
62
- blockers: [blocker]
63
- };
64
- }