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
@@ -0,0 +1,203 @@
1
+ import path from 'node:path';
2
+ import { removeDesktopBridgeManagedConfig } from '../../../cli/install-helpers-codex-lb-config.js';
3
+ import { safeWriteCodexConfigToml } from '../../codex-runtime/codex-desktop-config-policy.js';
4
+ import { exists, readText } from '../../fsx.js';
5
+ import { bootstrapExistingDesktopBridgeService, desktopBridgeServiceStatus, installAndStartDesktopBridgeService, stopDesktopBridgeService } from '../desktop-service.js';
6
+ import { rollbackDesktopBridgeUnificationReceipt } from '../migration-receipt.js';
7
+ import { resolveBridgeRequestRoute } from '../request-route-resolver.js';
8
+ import { setBridgeRoutingDefault, writeBridgeRoutingPolicy } from '../provider-route-policy.js';
9
+ import { syncCatalogInternal } from './catalog.js';
10
+ import { commandResult, controllerPaths, nowIso, persistRuntimeSettings, providerCode, providerRegistrySnapshot, stringArray } from './shared.js';
11
+ import { desktopBridgeStatusV3, loadCore, statusFromCore } from './status.js';
12
+ import { verifyDesktopBridgeV3 } from './verification.js';
13
+ export async function ensureDesktopBridge(options, operation) {
14
+ const sync = await syncCatalogInternal(options);
15
+ let core = await loadCore(options);
16
+ if (!core.activeCatalog.ok || !core.policy) {
17
+ return commandResult(operation, true, statusFromCore(core, options), { catalog_sync: sync }, syncResultBlockers(sync), options);
18
+ }
19
+ const service = await (options.installServiceImpl || installAndStartDesktopBridgeService)({
20
+ ...options,
21
+ home: core.paths.home,
22
+ providerRegistry: providerRegistrySnapshot(core.registry, core.activeCatalog.route_index),
23
+ routePolicy: core.policy
24
+ });
25
+ core = await loadCore(options);
26
+ let report = null;
27
+ if (service.running)
28
+ report = await verifyDesktopBridgeV3('shallow', options);
29
+ const status = await desktopBridgeStatusV3(options);
30
+ return commandResult(operation, true, status, { service, catalog_sync: sync, capabilities: report }, [], options);
31
+ }
32
+ export async function repairDesktopBridge(options) {
33
+ let core = await loadCore(options);
34
+ if (!core.activeCatalog.ok || !core.policy)
35
+ return ensureDesktopBridge(options, 'repair');
36
+ await persistRuntimeSettings(core, options);
37
+ const service = await (options.installServiceImpl || installAndStartDesktopBridgeService)({
38
+ ...options,
39
+ home: core.paths.home,
40
+ providerRegistry: providerRegistrySnapshot(core.registry, core.activeCatalog.route_index),
41
+ routePolicy: core.policy
42
+ });
43
+ core = await loadCore(options);
44
+ const report = service.running ? await verifyDesktopBridgeV3('shallow', options) : null;
45
+ const status = await desktopBridgeStatusV3(options);
46
+ return commandResult('repair', true, status, { service, capabilities: report }, [], options);
47
+ }
48
+ export async function setDefaultProvider(providerId, options) {
49
+ const core = await loadCore(options);
50
+ if (!core.policy || !core.activeCatalog.ok)
51
+ throw new Error('bridge_route_policy_missing');
52
+ if (!core.registry.profiles[providerId].enabled)
53
+ throw new Error(`${providerCode(providerId)}_provider_disabled`);
54
+ if (!Object.values(core.policy.model_routes).some((route) => route.provider_id === providerId)) {
55
+ throw new Error(`${providerCode(providerId)}_catalog_route_not_ready`);
56
+ }
57
+ const policy = setBridgeRoutingDefault(core.policy, providerId, nowIso(options));
58
+ await writeBridgeRoutingPolicy(core.paths.routePolicyPath, policy, core.activeCatalog.route_index);
59
+ await persistRuntimeSettings({ ...core, policy, policyBlockers: [] }, options);
60
+ const status = await desktopBridgeStatusV3(options);
61
+ return commandResult('route.set-default', true, status, { provider_id: providerId, policy_generation: policy.policy_generation }, [], options);
62
+ }
63
+ export async function explainRoute(model, options) {
64
+ const core = await loadCore(options);
65
+ if (!core.policy || !core.activeCatalog.ok)
66
+ throw new Error('bridge_route_policy_missing');
67
+ const explanation = resolveBridgeRequestRoute({ model }, core.policy, {
68
+ route_index: core.activeCatalog.route_index,
69
+ registry: core.registry,
70
+ active_catalog_generation: core.activeCatalog.catalog.generation
71
+ });
72
+ return commandResult('route.explain', true, statusFromCore(core, options), { explanation }, [], options);
73
+ }
74
+ export async function unmanageDesktopBridge(options) {
75
+ const paths = controllerPaths(options);
76
+ const current = await readText(paths.configPath, '');
77
+ const next = removeDesktopBridgeManagedConfig(current);
78
+ const serviceBefore = await currentServiceStatus(options, paths.home);
79
+ const stopped = await (options.stopServiceImpl || stopDesktopBridgeService)({
80
+ ...options,
81
+ home: paths.home
82
+ });
83
+ requireStopped(stopped, 'unmanage');
84
+ let write;
85
+ try {
86
+ write = await (options.safeWriteConfigImpl || safeWriteCodexConfigToml)(paths.configPath, current, next, 'desktop-bridge-unmanage', { verifyUnchangedBeforeWrite: true });
87
+ }
88
+ catch (error) {
89
+ await restartPreservedService(serviceBefore, error, options, paths.home);
90
+ throw error;
91
+ }
92
+ if (!write.ok) {
93
+ const error = new Error(`desktop_bridge_unmanage_config_${write.status}`);
94
+ const recovery = await restartPreservedService(serviceBefore, error, options, paths.home);
95
+ const status = await desktopBridgeStatusV3(options);
96
+ return commandResult('unmanage', false, status, { write, service: stopped, recovery }, [error.message], options);
97
+ }
98
+ const cleaned = await removePreservedServiceArtifacts(options, paths.home, 'unmanage');
99
+ const status = await desktopBridgeStatusV3(options);
100
+ requireUnmanaged(status.management.managed, 'unmanage');
101
+ return commandResult('unmanage', true, status, {
102
+ unmanaged: true,
103
+ credentials_deleted: false,
104
+ service: cleaned,
105
+ stopped_service: stopped,
106
+ config_backup_path: write.backup_path || null
107
+ }, [], options);
108
+ }
109
+ export async function rollbackDesktopBridge(receiptId, options) {
110
+ if (!/^[A-Za-z0-9._-]{1,160}$/.test(receiptId))
111
+ throw new Error('desktop_bridge_receipt_id_invalid');
112
+ const paths = controllerPaths(options);
113
+ const receiptPath = path.resolve(paths.receiptDir, `${receiptId.replace(/\.json$/i, '')}.json`);
114
+ if (!receiptPath.startsWith(`${path.resolve(paths.receiptDir)}${path.sep}`)) {
115
+ throw new Error('desktop_bridge_receipt_path_invalid');
116
+ }
117
+ const serviceBefore = await currentServiceStatus(options, paths.home);
118
+ const stopped = await (options.stopServiceImpl || stopDesktopBridgeService)({
119
+ ...options,
120
+ home: paths.home
121
+ });
122
+ requireStopped(stopped, 'rollback');
123
+ let rollback;
124
+ try {
125
+ rollback = await (options.rollbackReceiptImpl || rollbackDesktopBridgeUnificationReceipt)({ receiptPath });
126
+ }
127
+ catch (error) {
128
+ await restartPreservedService(serviceBefore, error, options, paths.home);
129
+ throw error;
130
+ }
131
+ if (!rollback.ok) {
132
+ const error = new Error(`desktop_bridge_rollback_${rollback.status}`);
133
+ const recovery = await restartPreservedService(serviceBefore, error, options, paths.home);
134
+ const status = await desktopBridgeStatusV3(options);
135
+ return commandResult('rollback', false, status, { rollback, service: stopped, recovery }, [rollback.status], options);
136
+ }
137
+ const cleaned = await removePreservedServiceArtifacts(options, paths.home, 'rollback');
138
+ const observed = await desktopBridgeStatusV3(options);
139
+ if (observed.management.managed)
140
+ throw new Error('desktop_bridge_rollback_final_state_managed');
141
+ const status = {
142
+ ...observed,
143
+ management: {
144
+ ...observed.management,
145
+ reason: 'rollback_complete'
146
+ }
147
+ };
148
+ return commandResult('rollback', true, status, { rollback, service: cleaned, stopped_service: stopped }, [], options);
149
+ }
150
+ async function currentServiceStatus(options, home) {
151
+ return (options.serviceStatusImpl || desktopBridgeServiceStatus)({ ...options, home });
152
+ }
153
+ function requireStopped(service, operation) {
154
+ if (service.running)
155
+ throw new Error(`desktop_bridge_${operation}_service_stop_failed`);
156
+ }
157
+ async function removePreservedServiceArtifacts(options, home, operation) {
158
+ const cleaned = await (options.stopServiceImpl || stopDesktopBridgeService)({
159
+ ...options,
160
+ home,
161
+ removePlist: true,
162
+ removeSettings: true
163
+ });
164
+ const artifactsRemain = await Promise.all([
165
+ exists(cleaned.paths.launch_agent_path),
166
+ exists(cleaned.paths.settings_path)
167
+ ]);
168
+ if (cleaned.running || cleaned.installed || cleaned.settings || artifactsRemain.some(Boolean)) {
169
+ throw new Error(`desktop_bridge_${operation}_service_cleanup_failed`);
170
+ }
171
+ return cleaned;
172
+ }
173
+ async function restartPreservedService(serviceBefore, originalError, options, home) {
174
+ if (!serviceBefore.running)
175
+ return null;
176
+ try {
177
+ const recovery = await (options.bootstrapServiceImpl || bootstrapExistingDesktopBridgeService)({
178
+ ...options,
179
+ home
180
+ });
181
+ if (!recovery.running) {
182
+ throw new Error(`desktop_bridge_lifecycle_recovery_not_running:${recovery.blockers.join(',')}`);
183
+ }
184
+ return recovery;
185
+ }
186
+ catch (recoveryError) {
187
+ const message = originalError instanceof Error ? originalError.message : String(originalError || 'desktop_bridge_lifecycle_failed');
188
+ throw new Error(message, { cause: recoveryError });
189
+ }
190
+ }
191
+ function requireUnmanaged(managed, operation) {
192
+ if (managed)
193
+ throw new Error(`desktop_bridge_${operation}_final_state_managed`);
194
+ }
195
+ function syncResultBlockers(result) {
196
+ if (result.ok === true)
197
+ return [];
198
+ const activation = result.activation && typeof result.activation === 'object' && !Array.isArray(result.activation)
199
+ ? result.activation
200
+ : {};
201
+ const blockers = stringArray(activation.blockers);
202
+ return blockers.length > 0 ? blockers : ['combined_catalog_sync_failed'];
203
+ }
@@ -0,0 +1,293 @@
1
+ import { DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PATH, probeDesktopBridgeWebSocket } from '../desktop-bridge/index.js';
2
+ import { runImageGenerationProbeV3 } from '../probes/image-generation-probe.js';
3
+ import { runComputerUseProbeV3 } from '../probes/computer-use-probe.js';
4
+ import { runVoiceRealtimeProbeV3 } from '../probes/voice-realtime-probe.js';
5
+ import { runAuxiliarySurfacesProbeV3 } from '../probes/auxiliary-surfaces-probe.js';
6
+ import { capabilityProbeResultV3 } from '../probes/probe-evidence.js';
7
+ import { validateCapabilityDeepEvidenceV2 } from '../trusted-deep-evidence.js';
8
+ import { bridgeClientUrl, providerCode, safeCode, timeoutMs, unique } from './shared.js';
9
+ export async function probeProviderText(core, providerId, loopbackOrigin, context, options) {
10
+ const route = core.policy
11
+ ? Object.entries(core.policy.model_routes).find(([, target]) => target.provider_id === providerId)
12
+ : null;
13
+ if (!loopbackOrigin || !route) {
14
+ return capabilityProbeResultV3({
15
+ ...context,
16
+ capability: 'text_responses',
17
+ scope: `provider:${providerId}`,
18
+ stage: 'feature_request',
19
+ state: 'not_attempted',
20
+ retryable: true,
21
+ recoveryAction: 'repair_bridge_service',
22
+ source: 'transport',
23
+ evidence: { provider_id: providerId, reason: 'bridge_or_route_unavailable' }
24
+ });
25
+ }
26
+ const publicModel = route[0];
27
+ const request = options.fetchImpl || globalThis.fetch;
28
+ let endpoint;
29
+ try {
30
+ endpoint = await bridgeClientUrl(loopbackOrigin, '/backend-api/codex/responses', options);
31
+ }
32
+ catch (error) {
33
+ const root = safeCode(error, 'desktop_bridge_client_capability_invalid');
34
+ return capabilityProbeResultV3({
35
+ ...context,
36
+ capability: 'text_responses',
37
+ scope: `provider:${providerId}`,
38
+ stage: 'feature_request',
39
+ state: 'blocked',
40
+ terminal: true,
41
+ rootCause: root,
42
+ blockers: [root],
43
+ retryable: true,
44
+ recoveryAction: 'repair_bridge_service',
45
+ source: 'transport',
46
+ evidence: { provider_id: providerId, public_model: publicModel, fallback: 'none' }
47
+ });
48
+ }
49
+ const body = {
50
+ model: publicModel,
51
+ input: 'Reply with OK.',
52
+ max_output_tokens: 1,
53
+ store: false,
54
+ ...(providerId === 'openrouter' ? { provider: { allow_fallbacks: false } } : {})
55
+ };
56
+ try {
57
+ const response = await request(endpoint, {
58
+ method: 'POST',
59
+ redirect: 'error',
60
+ headers: {
61
+ 'content-type': 'application/json',
62
+ accept: 'application/json',
63
+ origin: 'app://codex',
64
+ 'x-sks-model': publicModel
65
+ },
66
+ body: JSON.stringify(body),
67
+ signal: AbortSignal.timeout(timeoutMs(options))
68
+ });
69
+ const text = await response.text();
70
+ if (Buffer.byteLength(text) > 4 * 1024 * 1024)
71
+ throw new Error('provider_text_response_too_large');
72
+ let payload = null;
73
+ try {
74
+ payload = text ? JSON.parse(text) : null;
75
+ }
76
+ catch {
77
+ payload = null;
78
+ }
79
+ const valid = response.ok && payload !== null && typeof payload === 'object';
80
+ const root = valid ? null : `${providerCode(providerId)}_text_response_failed`;
81
+ return capabilityProbeResultV3({
82
+ ...context,
83
+ capability: 'text_responses',
84
+ scope: `provider:${providerId}`,
85
+ stage: valid ? 'complete' : 'feature_response',
86
+ state: valid ? 'verified' : 'blocked',
87
+ terminal: !valid,
88
+ rootCause: root,
89
+ blockers: root ? [root] : [],
90
+ retryable: !valid,
91
+ recoveryAction: valid ? null : 'retry_provider_transport_probe',
92
+ source: 'transport',
93
+ evidence: {
94
+ provider_id: providerId,
95
+ public_model: publicModel,
96
+ http_status: response.status,
97
+ response_object: valid,
98
+ fallback: 'none'
99
+ }
100
+ });
101
+ }
102
+ catch {
103
+ const root = `${providerCode(providerId)}_text_response_failed`;
104
+ return capabilityProbeResultV3({
105
+ ...context,
106
+ capability: 'text_responses',
107
+ scope: `provider:${providerId}`,
108
+ stage: 'feature_request',
109
+ state: 'failed',
110
+ terminal: true,
111
+ rootCause: root,
112
+ blockers: [root],
113
+ retryable: true,
114
+ recoveryAction: 'retry_provider_transport_probe',
115
+ source: 'transport',
116
+ evidence: { provider_id: providerId, public_model: publicModel, fallback: 'none' }
117
+ });
118
+ }
119
+ }
120
+ export async function probeProviderDeep(core, providerId, context, options) {
121
+ const endpoint = core.registry.profiles[providerId].endpoint.url || '';
122
+ const catalogGeneration = core.activeCatalog.ok ? core.activeCatalog.catalog.generation : '';
123
+ return runDesktopBridgeDeepProviderProbesV3(providerId, context, endpoint, catalogGeneration, options.deepProbeImpl);
124
+ }
125
+ export async function runDesktopBridgeDeepProviderProbesV3(providerId, context, endpoint, catalogGeneration, probeImpl) {
126
+ let evidence = null;
127
+ let adapterFailed = false;
128
+ if (probeImpl && endpoint && catalogGeneration) {
129
+ try {
130
+ evidence = await probeImpl({
131
+ provider_id: providerId,
132
+ report_id: context.reportId,
133
+ correlation_id: context.correlationId,
134
+ session_id: context.sessionId,
135
+ checked_at: context.checkedAt,
136
+ catalog_generation: catalogGeneration,
137
+ endpoint
138
+ });
139
+ }
140
+ catch {
141
+ adapterFailed = true;
142
+ }
143
+ }
144
+ const providerContext = { ...context, providerId };
145
+ const results = [
146
+ runImageGenerationProbeV3({ ...providerContext, ...(evidence?.image_generation || {}) }),
147
+ runComputerUseProbeV3({ ...providerContext, ...(evidence?.computer_use || {}) }),
148
+ runVoiceRealtimeProbeV3({ ...providerContext, ...(evidence?.voice_mode || {}) }),
149
+ runAuxiliarySurfacesProbeV3({ ...providerContext, ...(evidence?.auxiliary_surfaces || {}) })
150
+ ];
151
+ if (adapterFailed) {
152
+ for (const result of results)
153
+ result.warnings = unique([...result.warnings, 'deep_probe_adapter_failed']);
154
+ }
155
+ for (const [capability, trusted] of Object.entries(evidence?.trusted || {})) {
156
+ if (!trusted || results.some((result) => result.capability === capability))
157
+ continue;
158
+ const validation = validateCapabilityDeepEvidenceV2(trusted.envelope, {
159
+ expectedProviderId: providerId,
160
+ expectedScope: `provider:${providerId}`,
161
+ expectedCapability: capability,
162
+ expectedReportId: context.reportId,
163
+ expectedCatalogGeneration: catalogGeneration,
164
+ expectedEndpoint: endpoint,
165
+ trustAnchors: trusted.trust_anchors,
166
+ now: context.checkedAt
167
+ });
168
+ const rootCause = validation.state === 'blocked'
169
+ ? validation.blockers[0] || 'capability_deep_evidence_invalid' : null;
170
+ results.push(capabilityProbeResultV3({
171
+ ...providerContext,
172
+ capability,
173
+ scope: `provider:${providerId}`,
174
+ stage: validation.state === 'verified' ? 'complete' : 'artifact_validation',
175
+ state: validation.state,
176
+ terminal: validation.state === 'blocked',
177
+ rootCause,
178
+ blockers: validation.blockers,
179
+ warnings: validation.warnings,
180
+ retryable: validation.state === 'blocked' || validation.state === 'stale',
181
+ recoveryAction: validation.state === 'verified' ? null : 'run_deep_verification',
182
+ source: validation.state === 'verified' ? 'deep_probe' : 'artifact',
183
+ evidence: {
184
+ ...(validation.evidence || {}),
185
+ producer_id: validation.producer_id,
186
+ trust_anchor_id: validation.trust_anchor_id,
187
+ content_sha256: validation.content_sha256
188
+ }
189
+ }));
190
+ }
191
+ return results;
192
+ }
193
+ export async function probeBridgeHttp(loopbackOrigin, options) {
194
+ const started = Date.now();
195
+ if (!loopbackOrigin)
196
+ return httpFailure('desktop_bridge_tcp_connect_failed', 'tcp_connect', null, started);
197
+ try {
198
+ const endpoint = await bridgeClientUrl(loopbackOrigin, DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH, options);
199
+ const response = await (options.fetchImpl || globalThis.fetch)(endpoint, {
200
+ method: 'GET',
201
+ redirect: 'error',
202
+ headers: { accept: 'application/json', origin: 'app://codex' },
203
+ signal: AbortSignal.timeout(timeoutMs(options))
204
+ });
205
+ const payload = await response.json().catch(() => null);
206
+ if (!response.ok)
207
+ return httpFailure('desktop_bridge_http_health_failed', 'http_health', response.status, started);
208
+ if (payload?.schema !== 'sks.desktop-bridge-health.v1' || payload.runtime !== 'desktop-bridge') {
209
+ return httpFailure('desktop_bridge_http_health_invalid', 'http_health', response.status, started);
210
+ }
211
+ return {
212
+ schema: 'sks.desktop-bridge-http-probe.v1',
213
+ state: 'verified',
214
+ terminal_stage: 'complete',
215
+ root_cause: null,
216
+ status_code: response.status,
217
+ latency_ms: Date.now() - started,
218
+ blockers: [],
219
+ warnings: []
220
+ };
221
+ }
222
+ catch (error) {
223
+ const capabilityBlocker = safeCode(error, '');
224
+ if (capabilityBlocker.startsWith('desktop_bridge_client_capability_')) {
225
+ return httpFailure(capabilityBlocker, 'http_health', null, started);
226
+ }
227
+ const timedOut = error instanceof Error && (error.name === 'TimeoutError' || error.name === 'AbortError');
228
+ return httpFailure(timedOut ? 'desktop_bridge_http_health_timeout' : 'desktop_bridge_tcp_connect_failed', timedOut ? 'http_health' : 'tcp_connect', null, started);
229
+ }
230
+ }
231
+ function httpFailure(rootCause, stage, statusCode, started) {
232
+ return {
233
+ schema: 'sks.desktop-bridge-http-probe.v1',
234
+ state: 'failed',
235
+ terminal_stage: stage,
236
+ root_cause: rootCause,
237
+ status_code: statusCode,
238
+ latency_ms: Date.now() - started,
239
+ blockers: [rootCause],
240
+ warnings: []
241
+ };
242
+ }
243
+ export async function probeBridgeWebSocket(loopbackOrigin, level, options) {
244
+ if (!loopbackOrigin) {
245
+ return {
246
+ schema: 'sks.desktop-bridge-websocket-probe.v2',
247
+ state: 'failed',
248
+ terminal_stage: 'tcp_connect',
249
+ root_cause: 'desktop_bridge_tcp_connect_failed',
250
+ status_code: null,
251
+ negotiated_protocol: null,
252
+ upgrade_verified: false,
253
+ protocol_verified: false,
254
+ frame_round_trip_verified: false,
255
+ clean_close_verified: false,
256
+ latency_ms: null,
257
+ blockers: ['desktop_bridge_tcp_connect_failed'],
258
+ warnings: []
259
+ };
260
+ }
261
+ const websocketOrigin = loopbackOrigin.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:');
262
+ let url;
263
+ try {
264
+ url = await bridgeClientUrl(websocketOrigin, DESKTOP_BRIDGE_DIAGNOSTIC_PATH, options);
265
+ }
266
+ catch (error) {
267
+ return websocketFailure(safeCode(error, 'desktop_bridge_client_capability_invalid'));
268
+ }
269
+ return probeDesktopBridgeWebSocket({
270
+ url,
271
+ origin: 'app://codex',
272
+ requestedLevel: level,
273
+ maxRetries: 2,
274
+ totalTimeoutMs: timeoutMs(options)
275
+ });
276
+ }
277
+ function websocketFailure(rootCause) {
278
+ return {
279
+ schema: 'sks.desktop-bridge-websocket-probe.v2',
280
+ state: 'failed',
281
+ terminal_stage: 'tcp_connect',
282
+ root_cause: rootCause,
283
+ status_code: null,
284
+ negotiated_protocol: null,
285
+ upgrade_verified: false,
286
+ protocol_verified: false,
287
+ frame_round_trip_verified: false,
288
+ clean_close_verified: false,
289
+ latency_ms: null,
290
+ blockers: [rootCause],
291
+ warnings: []
292
+ };
293
+ }
@@ -0,0 +1,144 @@
1
+ import { capabilityProbeResultV3 } from '../probes/probe-evidence.js';
2
+ import { activeProviderIds, providerCode } from './shared.js';
3
+ export function nativeIdentityProbe(core, context) {
4
+ const oauth = core.auth.mode === 'chatgpt_oauth' || core.auth.mode === 'mixed';
5
+ return capabilityProbeResultV3({
6
+ ...context,
7
+ capability: 'oauth_identity',
8
+ scope: 'native-identity',
9
+ stage: 'preflight',
10
+ state: oauth ? 'verified' : 'blocked',
11
+ terminal: !oauth,
12
+ rootCause: oauth ? null : 'chatgpt_oauth_required_for_desktop',
13
+ blockers: oauth ? [] : ['chatgpt_oauth_required_for_desktop'],
14
+ retryable: false,
15
+ recoveryAction: oauth ? null : 'review_desktop_authentication',
16
+ source: 'config',
17
+ evidence: {
18
+ oauth_present: oauth,
19
+ semantic_identity_available: Boolean(core.auth.semantic_fingerprint),
20
+ oauth_requirement: 'required'
21
+ }
22
+ });
23
+ }
24
+ export function combinedRoutePolicyProbe(core, context) {
25
+ const verified = Boolean(core.policy && core.activeCatalog.ok
26
+ && core.policy.catalog_generation === core.activeCatalog.catalog.generation
27
+ && core.policyBlockers.length === 0);
28
+ return capabilityProbeResultV3({
29
+ ...context,
30
+ capability: 'route_policy',
31
+ scope: 'catalog:combined',
32
+ stage: verified ? 'complete' : 'model_route',
33
+ state: verified ? 'verified' : 'blocked',
34
+ terminal: !verified,
35
+ rootCause: verified ? null : String(core.policyBlockers[0] || 'bridge_route_policy_missing'),
36
+ blockers: verified ? [] : [...core.policyBlockers],
37
+ retryable: !verified,
38
+ recoveryAction: verified ? null : 'refresh_catalog_or_select_supported_model',
39
+ source: 'config',
40
+ evidence: {
41
+ catalog_generation: core.activeCatalog.ok ? core.activeCatalog.catalog.generation : null,
42
+ route_policy_generation: core.policy?.policy_generation || null,
43
+ fallback: 'none'
44
+ }
45
+ });
46
+ }
47
+ export function combinedModelRouteProbe(core, status, context) {
48
+ const active = activeProviderIds(core);
49
+ const verified = active.length > 0 && active.every((providerId) => Boolean(core.policy && Object.values(core.policy.model_routes).some((route) => route.provider_id === providerId)));
50
+ return capabilityProbeResultV3({
51
+ ...context,
52
+ capability: 'model_route',
53
+ scope: 'catalog:combined',
54
+ stage: verified ? 'complete' : 'model_route',
55
+ state: verified ? 'verified' : 'blocked',
56
+ terminal: !verified,
57
+ rootCause: verified ? null : 'catalog_model_route_missing',
58
+ blockers: verified ? [] : ['catalog_model_route_missing'],
59
+ retryable: !verified,
60
+ recoveryAction: verified ? null : 'refresh_catalog_or_select_supported_model',
61
+ source: 'config',
62
+ evidence: {
63
+ active_provider_ids: active,
64
+ route_count: status.catalog_sync.route_count,
65
+ fallback: 'none'
66
+ }
67
+ });
68
+ }
69
+ export function providerCredentialProbe(core, providerId, context) {
70
+ const profile = core.registry.profiles[providerId];
71
+ const ready = profile.credential.state === 'ready';
72
+ const blocked = ['rejected', 'unavailable', 'stale'].includes(profile.credential.state);
73
+ const root = blocked ? profile.credential.blockers[0] || `${providerCode(providerId)}_credential_unavailable` : null;
74
+ return capabilityProbeResultV3({
75
+ ...context,
76
+ capability: 'credential',
77
+ scope: `provider:${providerId}`,
78
+ stage: 'preflight',
79
+ state: ready ? 'verified' : blocked ? 'blocked' : 'not_attempted',
80
+ terminal: blocked,
81
+ rootCause: root,
82
+ blockers: root ? [root] : [],
83
+ warnings: profile.credential.warnings,
84
+ retryable: blocked,
85
+ recoveryAction: ready ? null : providerId === 'codex-lb'
86
+ ? 'configure_codex_lb_credential' : 'configure_openrouter_credential',
87
+ source: ready ? 'transport' : 'config',
88
+ evidence: {
89
+ provider_id: providerId,
90
+ credential_state: profile.credential.state,
91
+ credential_fingerprint: profile.credential.fingerprint,
92
+ route: providerId
93
+ }
94
+ });
95
+ }
96
+ export function providerAuthProbe(core, providerId, context) {
97
+ const profile = core.registry.profiles[providerId];
98
+ const verified = profile.credential.state === 'ready' && core.catalogSync.providers[providerId].state === 'verified';
99
+ const blocked = profile.enabled && ['rejected', 'unavailable', 'stale'].includes(profile.credential.state);
100
+ const root = blocked ? profile.credential.blockers[0] || `${providerCode(providerId)}_auth_rejected` : null;
101
+ return capabilityProbeResultV3({
102
+ ...context,
103
+ capability: 'provider_auth',
104
+ scope: `provider:${providerId}`,
105
+ stage: verified ? 'complete' : 'provider_auth',
106
+ state: verified ? 'verified' : blocked ? 'blocked' : 'not_attempted',
107
+ terminal: blocked,
108
+ rootCause: root,
109
+ blockers: root ? [root] : [],
110
+ retryable: blocked,
111
+ recoveryAction: verified ? null : providerId === 'codex-lb'
112
+ ? 'configure_codex_lb_credential' : 'configure_openrouter_credential',
113
+ source: verified || blocked ? 'transport' : 'config',
114
+ evidence: {
115
+ provider_id: providerId,
116
+ auth_transport: profile.endpoint.auth_transport,
117
+ credential_fingerprint: profile.credential.fingerprint,
118
+ oauth_forwarded: false
119
+ }
120
+ });
121
+ }
122
+ export function providerModelRouteProbe(core, providerId, context) {
123
+ const route = core.policy
124
+ ? Object.entries(core.policy.model_routes).find(([, target]) => target.provider_id === providerId)
125
+ : null;
126
+ const verified = Boolean(route && core.catalogSync.providers[providerId].state === 'verified');
127
+ return capabilityProbeResultV3({
128
+ ...context,
129
+ capability: 'model_route',
130
+ scope: `provider:${providerId}`,
131
+ stage: verified ? 'complete' : 'model_route',
132
+ state: verified ? 'verified' : 'not_attempted',
133
+ retryable: !verified,
134
+ recoveryAction: verified ? null : 'refresh_catalog_or_select_supported_model',
135
+ source: 'config',
136
+ evidence: {
137
+ provider_id: providerId,
138
+ public_model: route?.[0] || null,
139
+ upstream_model: route?.[1].upstream_model || null,
140
+ catalog_generation: core.policy?.catalog_generation || null,
141
+ fallback: 'none'
142
+ }
143
+ });
144
+ }