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,11 +1,7 @@
1
1
  import { madHighCommand } from '../core/commands/mad-sks-command.js';
2
- import { maybePromptCodexUpdateForLaunch, maybePromptCodexLbSetupForLaunch, maybePromptSksUpdateForLaunch } from '../cli/install-helpers.js';
3
2
  import { PACKAGE_VERSION } from '../core/fsx.js';
4
3
  export async function run(_command, args = []) {
5
4
  return madHighCommand(['--mad-sks', ...args], {
6
- maybePromptSksUpdateForLaunch,
7
- maybePromptCodexUpdateForLaunch,
8
- maybePromptCodexLbSetupForLaunch,
9
5
  packageVersion: PACKAGE_VERSION
10
6
  });
11
7
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "8.1.2",
3
+ "package_version": "8.2.0",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -1,5 +1,5 @@
1
1
  import { codexModelEffortCapability } from '../codex-control/codex-model-capabilities.js';
2
- import { GLM_52_OPENROUTER_MODEL } from '../codex-app/openrouter-provider.js';
2
+ import { OPENROUTER_DEFAULT_MODEL } from '../codex-app/openrouter-provider.js';
3
3
  import { managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
4
4
  import { isNarutoGpt56Model } from '../provider/model-router.js';
5
5
  import { decideSubagentModel, subagentModelProfile } from '../subagents/model-policy.js';
@@ -231,13 +231,13 @@ export function decideAgentWorkerModel(input = {}) {
231
231
  const risky = HIGH_SIGNAL_RE.test(text) || XHIGH_SIGNAL_RE.test(text);
232
232
  const simple = SIMPLE_CODE_MOD_RE.test(simpleText) && !HIGH_SIGNAL_RE.test(simpleText) && !XHIGH_SIGNAL_RE.test(simpleText);
233
233
  if (glmMain) {
234
- const glmEffort = glmWorkerEffort({ effort, risky, simple });
234
+ const openRouterEffort = openRouterWorkerEffort({ effort, risky, simple });
235
235
  return {
236
- model: GLM_52_OPENROUTER_MODEL,
237
- model_reasoning_effort: glmEffort,
238
- model_tier: `glm-5.2-${glmEffort === 'none' ? 'minimal' : glmEffort}`,
239
- model_profile: glmProfileForReasoning(glmEffort),
240
- reason: `glm_52_${glmEffort}_worker`
236
+ model: OPENROUTER_DEFAULT_MODEL,
237
+ model_reasoning_effort: openRouterEffort,
238
+ model_tier: `glm-5.2-${openRouterEffort === 'none' ? 'minimal' : openRouterEffort}`,
239
+ model_profile: desktopBridgeOpenRouterProfile(openRouterEffort),
240
+ reason: `desktop_bridge_openrouter_${openRouterEffort}_worker`
241
241
  };
242
242
  }
243
243
  const modelEffort = risky || effort === 'high' || effort === 'xhigh'
@@ -259,11 +259,11 @@ function safeProfileSegment(value) {
259
259
  }
260
260
  function isGlmWorkerMode(mainModel) {
261
261
  const model = String(mainModel || '').trim().toLowerCase();
262
- return model === GLM_52_OPENROUTER_MODEL
262
+ return model === OPENROUTER_DEFAULT_MODEL
263
263
  || model === 'glm-5.2'
264
264
  || model === 'glm5.2';
265
265
  }
266
- function glmWorkerEffort(input) {
266
+ function openRouterWorkerEffort(input) {
267
267
  if (input.effort === 'xhigh')
268
268
  return 'xhigh';
269
269
  if (input.risky || input.effort === 'high')
@@ -272,16 +272,6 @@ function glmWorkerEffort(input) {
272
272
  return 'minimal';
273
273
  return 'low';
274
274
  }
275
- function glmProfileForReasoning(effort) {
276
- if (effort === 'xhigh')
277
- return 'sks-openrouter-xhigh';
278
- if (effort === 'high')
279
- return 'sks-openrouter-high';
280
- if (effort === 'medium')
281
- return 'sks-openrouter-medium';
282
- if (effort === 'low')
283
- return 'sks-openrouter-low';
284
- if (effort === 'none')
285
- return 'sks-openrouter-default';
286
- return 'sks-openrouter-minimal';
275
+ function desktopBridgeOpenRouterProfile(effort) {
276
+ return `desktop-bridge-openrouter-${effort}`;
287
277
  }
@@ -74,6 +74,32 @@ export function scanAgentTextForRecursion(text) {
74
74
  warning: violations.length ? 'agent_worker_recursion_attempt_blocked' : null
75
75
  };
76
76
  }
77
+ export function agentWorkerHookRecursionDecision(state = {}, payload = {}, command = '') {
78
+ if (!agentWorkerHookContext(state, payload))
79
+ return null;
80
+ const guard = scanAgentTextForRecursion(command);
81
+ if (guard.ok)
82
+ return null;
83
+ return {
84
+ decision: 'block',
85
+ permissionDecision: 'deny',
86
+ reason: `Agent command recursion guard blocked nested SKS route command in Codex PreToolUse hook: ${guard.violations.join(', ')}`
87
+ };
88
+ }
89
+ export function agentWorkerHookContext(state = {}, payload = {}) {
90
+ const env = {
91
+ ...(payload.env || {}),
92
+ ...(payload.tool_input?.env || {}),
93
+ ...(payload.toolInput?.env || {}),
94
+ ...(payload.input?.env || {}),
95
+ ...(payload.tool?.input?.env || {})
96
+ };
97
+ void state;
98
+ return Boolean(String(env.SKS_AGENT_WORKER || '') === '1'
99
+ || String(env.SKS_DISABLE_ROUTE_RECURSION || '') === '1'
100
+ || payload.agent_worker === true
101
+ || payload.agentWorker === true);
102
+ }
77
103
  export function assertNoAgentRecursion(text) {
78
104
  const result = scanAgentTextForRecursion(text);
79
105
  if (!result.ok)
@@ -4,7 +4,7 @@ import { managedProxyEnvForChild } from '../codex/managed-proxy-env.js';
4
4
  import { agentWorkerEnv, validateAgentWorkerResult } from './agent-worker-pipeline.js';
5
5
  import { fastModeEnv, resolveFastModePolicy } from './fast-mode-policy.js';
6
6
  import { buildCodexExecArgs } from '../codex/codex-cli-syntax-builder.js';
7
- import { codexLbRecoveryBlockedProcessResult, withCodexLbCliLaunchRecovery } from '../codex-control/codex-lb-launch-recovery.js';
7
+ import { desktopBridgeLaunchBlockedProcessResult, withDesktopBridgeCliLaunchGuard } from '../codex-control/desktop-bridge-launch-guard.js';
8
8
  import { prepareCodexAppServerRuntimeEnv } from '../codex-control/codex-app-server-runtime-env.js';
9
9
  export function buildCodexExecAgentArgs(agent, prompt, opts = {}) {
10
10
  const resultFile = opts.resultFile || defaultCodexResultFile(agent, opts);
@@ -78,16 +78,14 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
78
78
  const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: effectiveEnv });
79
79
  const proxyEnv = managedProxyEnvForChild(runtimeEnv);
80
80
  const execute = opts.runProcessImpl || runProcess;
81
- const guarded = await withCodexLbCliLaunchRecovery({
81
+ const guarded = await withDesktopBridgeCliLaunchGuard({
82
82
  root: opts.cwd || process.cwd(),
83
83
  env: runtimeEnv,
84
- cliArgs: command.args.slice(0, -1),
85
- ...(typeof opts.recoveryFetch === 'function' ? { fetchImpl: opts.recoveryFetch } : {}),
86
- ...(opts.recoveryTimeoutMs === undefined ? {} : { timeoutMs: opts.recoveryTimeoutMs })
87
- }, () => execute(opts.codexBin || 'codex', command.args, { cwd: opts.cwd || process.cwd(), env: { ...runtimeEnv, ...proxyEnv, ...fastModeEnv(fastPolicy), ...workerEnv }, timeoutMs: opts.timeoutMs || 30 * 60 * 1000, maxOutputBytes: 256 * 1024, stdoutFile, stderrFile }));
84
+ cliArgs: command.args.slice(0, -1)
85
+ }, (sanitizedEnv) => execute(opts.codexBin || 'codex', command.args, { cwd: opts.cwd || process.cwd(), env: { ...sanitizedEnv, ...proxyEnv, ...fastModeEnv(fastPolicy), ...workerEnv }, timeoutMs: opts.timeoutMs || 30 * 60 * 1000, maxOutputBytes: 256 * 1024, stdoutFile, stderrFile }));
88
86
  const result = guarded.launched
89
87
  ? guarded.value
90
- : codexLbRecoveryBlockedProcessResult(guarded.toolOutputRecovery);
88
+ : desktopBridgeLaunchBlockedProcessResult(guarded.desktopBridgeLaunchGuard);
91
89
  const report = await writeCodexProcessReport(opts.agentRoot || opts.cwd || process.cwd(), agent, {
92
90
  project_hash: opts.projectHash || agent.project_hash || agent.root_hash || null,
93
91
  command: [opts.codexBin || 'codex', ...command.args],
@@ -113,7 +111,7 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
113
111
  stderr_bytes: result.stderrBytes,
114
112
  truncated: result.truncated,
115
113
  timed_out: result.timedOut,
116
- codex_lb_tool_output_recovery: guarded.toolOutputRecovery,
114
+ desktop_bridge_launch_guard: guarded.desktopBridgeLaunchGuard,
117
115
  dry_run: false
118
116
  });
119
117
  if (result.code === 0) {
@@ -138,7 +136,7 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
138
136
  return { ...validated, status: 'blocked', blockers: [...validated.blockers, 'codex_exec_result_schema_invalid'] };
139
137
  }
140
138
  }
141
- return validateAgentWorkerResult({ mission_id: opts.missionId || opts.mission_id || '', agent_id: agent.id, session_id: agent.session_id, persona_id: agent.persona_id || agent.id, task_slice_id: slice?.id || '', status: result.code === 0 ? 'done' : 'failed', backend: 'codex-exec', summary: result.stdout.slice(-1000) || result.stderr.slice(-1000), artifacts: [command.resultFile, report], blockers: result.code === 0 ? ['codex_exec_output_last_message_missing_or_invalid'] : [...(guarded.launched ? [] : guarded.toolOutputRecovery.blockers), 'codex_exec_exit_' + result.code], confidence: 'verified_partial', unverified: result.code === 0 ? ['codex-exec stdout fallback; resultFile JSON missing or invalid'] : [], writes: [], source_intelligence_refs: agent.source_intelligence_refs || null, goal_mode_ref: agent.goal_mode_ref || null, codex_child_report: { tool_output_recovery: guarded.toolOutputRecovery }, verification: { status: result.code === 0 ? 'partial' : 'failed', checks: ['codex-exec-exit-code', 'codex-exec-process-report', 'codex-exec-output-last-message'] } });
139
+ return validateAgentWorkerResult({ mission_id: opts.missionId || opts.mission_id || '', agent_id: agent.id, session_id: agent.session_id, persona_id: agent.persona_id || agent.id, task_slice_id: slice?.id || '', status: result.code === 0 ? 'done' : 'failed', backend: 'codex-exec', summary: result.stdout.slice(-1000) || result.stderr.slice(-1000), artifacts: [command.resultFile, report], blockers: result.code === 0 ? ['codex_exec_output_last_message_missing_or_invalid'] : [...(guarded.launched ? [] : guarded.desktopBridgeLaunchGuard.blockers), 'codex_exec_exit_' + result.code], confidence: 'verified_partial', unverified: result.code === 0 ? ['codex-exec stdout fallback; resultFile JSON missing or invalid'] : [], writes: [], source_intelligence_refs: agent.source_intelligence_refs || null, goal_mode_ref: agent.goal_mode_ref || null, codex_child_report: { desktop_bridge_launch_guard: guarded.desktopBridgeLaunchGuard }, verification: { status: result.code === 0 ? 'partial' : 'failed', checks: ['codex-exec-exit-code', 'codex-exec-process-report', 'codex-exec-output-last-message'] } });
142
140
  }
143
141
  function workerNoMcpDefault(agent, opts = {}, sandbox = 'read-only') {
144
142
  if (opts.noMcp !== undefined)
@@ -1,6 +1,9 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { BRIDGE_PROVIDER_IDS } from '../../codex-lb/bridge-contracts.js';
3
+ import { canonicalizeBridgeModelId, normalizeBridgeUpstreamModelId } from '../../codex-lb/route-index.js';
2
4
  import { canonicalJson } from '../../json/canonical.js';
3
- export const ARCHITECTURE_HARDENING_CONTRACT_VERSION = 'sks.architecture-hardening.v1';
5
+ export const ARCHITECTURE_HARDENING_CONTRACT_VERSION = 'sks.architecture-hardening.contracts.v2';
6
+ export const MAX_AUDIT_PROJECTION_DEPTH = 32;
4
7
  export class ArchitectureContractError extends Error {
5
8
  code;
6
9
  constructor(code) {
@@ -9,185 +12,246 @@ export class ArchitectureContractError extends Error {
9
12
  this.code = code;
10
13
  }
11
14
  }
12
- const PROVIDER_MODES = new Set(['chatgpt-oauth', 'codex-lb', 'openrouter']);
13
- const CREDENTIAL_CLASSES = new Set(['codex-native-oauth', 'codex-lb-api-key', 'openrouter-api-key']);
15
+ const HISTORICAL_SESSION_PIN_KEYS = [
16
+ 'schema',
17
+ 'session_id',
18
+ 'mode',
19
+ 'model',
20
+ 'credential_class',
21
+ 'allowed_models',
22
+ 'lb_affinity_token_hash',
23
+ 'child_policy_hash',
24
+ 'catalog_version',
25
+ 'parent_session_id'
26
+ ];
27
+ const CURRENT_SESSION_PIN_KEYS = [
28
+ 'thread_id',
29
+ 'provider_id',
30
+ 'public_model',
31
+ 'upstream_model',
32
+ 'catalog_generation',
33
+ 'route_policy_generation',
34
+ 'created_at'
35
+ ];
36
+ const PROVIDERS = new Set(BRIDGE_PROVIDER_IDS);
14
37
  const PROHIBITED_AUDIT_FIELDS = new Set([
38
+ 'account_id',
15
39
  'api_key',
16
- 'secret',
17
40
  'authorization',
18
- 'account_id',
19
- 'request_body',
41
+ 'client_secret',
42
+ 'cookie',
20
43
  'credential_fingerprint',
44
+ 'password',
21
45
  'refresh_token',
46
+ 'request_body',
47
+ 'secret',
48
+ 'set_cookie',
22
49
  'access_token'
23
50
  ]);
24
- const MAX_AUDIT_PROJECTION_DEPTH = 64;
25
- export function credentialClassForMode(mode) {
26
- if (mode === 'chatgpt-oauth')
27
- return 'codex-native-oauth';
28
- if (mode === 'codex-lb')
29
- return 'codex-lb-api-key';
30
- return 'openrouter-api-key';
31
- }
32
- export function stableArchitectureHash(value) {
33
- return createHash('sha256').update(canonicalJson(value)).digest('hex');
34
- }
35
- export function parseProviderPolicySnapshot(value) {
36
- const row = objectRecord(value, 'provider_policy_invalid');
37
- exactKeys(row, [
38
- 'schema', 'contract_version', 'mode', 'credential_class', 'allowed_models', 'child_policy_hash', 'catalog_version'
39
- ], 'provider_policy_unknown_field');
40
- if (row.schema !== 'sks.provider-policy-snapshot.v1')
41
- fail('provider_policy_schema_invalid');
42
- if (row.contract_version !== ARCHITECTURE_HARDENING_CONTRACT_VERSION)
43
- fail('provider_policy_version_mismatch');
44
- if (!PROVIDER_MODES.has(row.mode))
45
- fail('provider_policy_mode_invalid');
46
- if (!CREDENTIAL_CLASSES.has(row.credential_class))
47
- fail('provider_policy_credential_class_invalid');
48
- if (credentialClassForMode(row.mode) !== row.credential_class)
49
- fail('provider_policy_credential_class_mismatch');
50
- const models = stringArray(row.allowed_models, 'provider_policy_allowed_models_invalid');
51
- if (models.length !== new Set(models).size || models.some((model) => !model.trim()))
52
- fail('provider_policy_allowed_models_invalid');
53
- if (!safeHash(row.child_policy_hash))
54
- fail('provider_policy_child_hash_invalid');
55
- if (!safeVersion(row.catalog_version))
56
- fail('provider_policy_catalog_version_invalid');
57
- return value;
58
- }
59
- export function validateArchitectureConfiguration(value) {
60
- const row = objectRecord(value, 'architecture_configuration_invalid');
61
- exactKeys(row, ['schema', 'policy', 'credential', 'catalog', 'features'], 'architecture_configuration_unknown_field');
62
- if (row.schema !== 'sks.architecture-configuration.v1')
63
- fail('architecture_configuration_schema_invalid');
64
- parseProviderPolicySnapshot(row.policy);
65
- validateCredentialReadiness(row.credential);
66
- if (row.catalog !== null)
67
- validateCatalogSnapshot(row.catalog);
68
- if (!Array.isArray(row.features))
69
- fail('architecture_configuration_features_invalid');
70
- for (const feature of row.features)
71
- validateFeatureCompatibility(feature);
72
- assertSafeAuditProjection(value);
73
- return value;
74
- }
75
- export function assertProviderPolicyCompatible(expected, actual) {
76
- parseProviderPolicySnapshot(expected);
77
- parseProviderPolicySnapshot(actual);
78
- if (expected.mode !== actual.mode)
79
- fail('provider_policy_mode_mismatch');
80
- if (expected.credential_class !== actual.credential_class)
81
- fail('provider_policy_credential_mismatch');
82
- if (expected.child_policy_hash !== actual.child_policy_hash)
83
- fail('provider_policy_child_mismatch');
84
- if (expected.catalog_version !== actual.catalog_version)
85
- fail('provider_policy_catalog_mismatch');
86
- if (canonicalJson([...expected.allowed_models].sort()) !== canonicalJson([...actual.allowed_models].sort())) {
87
- fail('provider_policy_model_allowlist_mismatch');
51
+ export function decodeHistoricalSessionPinV1(value, context) {
52
+ const row = record(value);
53
+ if (!row || row.schema !== 'sks.session-pin.v1')
54
+ return decodeFailure('historical_session_pin_invalid');
55
+ if (!hasExactKeys(row, HISTORICAL_SESSION_PIN_KEYS))
56
+ return decodeFailure('historical_session_pin_unknown_field');
57
+ const provider = historicalProvider(row.mode);
58
+ if (!provider)
59
+ return decodeFailure('historical_session_pin_mode_unsupported');
60
+ if (row.credential_class !== credentialClassForProvider(provider)) {
61
+ return decodeFailure('historical_session_pin_credential_mismatch');
62
+ }
63
+ const sessionId = safeIdentifier(row.session_id);
64
+ const publicModel = canonicalizeBridgeModelId(row.model);
65
+ if (!sessionId || !publicModel)
66
+ return decodeFailure('historical_session_pin_model_invalid');
67
+ const allowedModels = canonicalModelArray(row.allowed_models);
68
+ if (!allowedModels || !allowedModels.includes(publicModel)) {
69
+ return decodeFailure('historical_session_pin_model_not_allowed');
70
+ }
71
+ if (!safeHash(row.child_policy_hash)
72
+ || (row.lb_affinity_token_hash !== null && !safeHash(row.lb_affinity_token_hash))) {
73
+ return decodeFailure('historical_session_pin_hash_invalid');
74
+ }
75
+ if (row.lb_affinity_token_hash !== null || row.parent_session_id !== null) {
76
+ return decodeFailure('historical_session_pin_semantics_unrepresentable');
88
77
  }
78
+ if (!validDecodeContext(context))
79
+ return decodeFailure('historical_session_pin_context_invalid');
80
+ if (provider !== context.current_provider_id)
81
+ return decodeFailure('historical_session_pin_provider_mismatch');
82
+ if (row.catalog_version !== context.current_catalog_generation) {
83
+ return decodeFailure('historical_session_pin_catalog_stale');
84
+ }
85
+ if (row.child_policy_hash !== context.expected_child_policy_hash) {
86
+ return decodeFailure('historical_session_pin_child_policy_mismatch');
87
+ }
88
+ const pin = {
89
+ thread_id: sessionId,
90
+ provider_id: provider,
91
+ public_model: publicModel,
92
+ upstream_model: context.current_upstream_model,
93
+ catalog_generation: context.current_catalog_generation,
94
+ route_policy_generation: context.current_route_policy_generation,
95
+ created_at: context.created_at
96
+ };
97
+ return validateCurrentProviderSessionPin(pin)
98
+ ? { ok: true, pin, blocker: null }
99
+ : decodeFailure('current_session_pin_invalid');
89
100
  }
90
- export function assertSafeAuditProjection(value) {
101
+ export function validateCurrentProviderSessionPin(value) {
102
+ const row = record(value);
103
+ if (!row || !hasExactKeys(row, CURRENT_SESSION_PIN_KEYS))
104
+ return false;
105
+ const publicModel = canonicalizeBridgeModelId(row.public_model);
106
+ const upstreamModel = normalizeBridgeUpstreamModelId(row.upstream_model);
107
+ return Boolean(safeIdentifier(row.thread_id)
108
+ && PROVIDERS.has(String(row.provider_id))
109
+ && publicModel
110
+ && publicModel === row.public_model
111
+ && upstreamModel
112
+ && upstreamModel === row.upstream_model
113
+ && safeGeneration(row.catalog_generation)
114
+ && safeGeneration(row.route_policy_generation)
115
+ && validIso(row.created_at));
116
+ }
117
+ export function createSafeAuditProjection(value) {
91
118
  const ancestors = new WeakSet();
92
- const visit = (current, depth) => {
93
- if (depth > MAX_AUDIT_PROJECTION_DEPTH)
94
- fail('audit_projection_depth_exceeded');
95
- if (Array.isArray(current)) {
96
- if (ancestors.has(current))
97
- fail('audit_projection_cycle');
98
- ancestors.add(current);
99
- try {
100
- for (const item of current)
101
- visit(item, depth + 1);
102
- }
103
- finally {
104
- ancestors.delete(current);
119
+ return projectAuditValue(value, 0, ancestors);
120
+ }
121
+ export function assertSafeAuditProjection(value) {
122
+ createSafeAuditProjection(value);
123
+ }
124
+ export function stableArchitectureHash(value) {
125
+ const projection = createSafeAuditProjection(value);
126
+ return createHash('sha256').update(canonicalJson(projection)).digest('hex');
127
+ }
128
+ export function jsonRoundTrip(value) {
129
+ return JSON.parse(JSON.stringify(createSafeAuditProjection(value)));
130
+ }
131
+ function projectAuditValue(value, depth, ancestors) {
132
+ if (depth > MAX_AUDIT_PROJECTION_DEPTH)
133
+ fail('audit_projection_depth_exceeded');
134
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
135
+ return value;
136
+ if (typeof value === 'number') {
137
+ if (!Number.isFinite(value))
138
+ fail('audit_projection_number_invalid');
139
+ return Object.is(value, -0) ? 0 : value;
140
+ }
141
+ if (typeof value !== 'object')
142
+ fail('audit_projection_value_unsupported');
143
+ if (ancestors.has(value))
144
+ fail('audit_projection_cycle');
145
+ ancestors.add(value);
146
+ try {
147
+ if (Array.isArray(value)) {
148
+ const ownKeys = Reflect.ownKeys(value);
149
+ if (ownKeys.some((key) => typeof key === 'symbol'
150
+ || (key !== 'length' && !/^(0|[1-9][0-9]*)$/.test(key)))) {
151
+ fail('audit_projection_array_property_unsupported');
105
152
  }
106
- return;
107
- }
108
- if (!current || typeof current !== 'object')
109
- return;
110
- if (ancestors.has(current))
111
- fail('audit_projection_cycle');
112
- ancestors.add(current);
113
- try {
114
- for (const [key, item] of Object.entries(current)) {
115
- if (PROHIBITED_AUDIT_FIELDS.has(key.toLowerCase()))
116
- fail('audit_projection_prohibited_field');
117
- visit(item, depth + 1);
153
+ const projected = [];
154
+ for (let index = 0; index < value.length; index += 1) {
155
+ if (!Object.prototype.hasOwnProperty.call(value, index))
156
+ fail('audit_projection_sparse_array');
157
+ projected.push(projectAuditValue(value[index], depth + 1, ancestors));
118
158
  }
159
+ return projected;
119
160
  }
120
- finally {
121
- ancestors.delete(current);
161
+ const prototype = Object.getPrototypeOf(value);
162
+ if (prototype !== Object.prototype && prototype !== null)
163
+ fail('audit_projection_object_unsupported');
164
+ if (Object.getOwnPropertySymbols(value).length > 0)
165
+ fail('audit_projection_symbol_key');
166
+ const projected = {};
167
+ const descriptors = Object.getOwnPropertyDescriptors(value);
168
+ for (const key of Object.keys(descriptors).sort()) {
169
+ const descriptor = descriptors[key];
170
+ if (!descriptor.enumerable)
171
+ continue;
172
+ if (!('value' in descriptor))
173
+ fail('audit_projection_accessor_unsupported');
174
+ if (isSensitiveAuditField(key))
175
+ fail('audit_projection_prohibited_field');
176
+ projected[key] = projectAuditValue(descriptor.value, depth + 1, ancestors);
122
177
  }
123
- };
124
- visit(value, 0);
178
+ return projected;
179
+ }
180
+ finally {
181
+ ancestors.delete(value);
182
+ }
125
183
  }
126
- export function jsonRoundTrip(value) {
127
- return JSON.parse(JSON.stringify(value));
128
- }
129
- function validateCredentialReadiness(value) {
130
- const row = objectRecord(value, 'credential_readiness_invalid');
131
- exactKeys(row, ['status', 'reason_code'], 'credential_readiness_unknown_field');
132
- const statuses = new Set(['ready', 'not_found', 'locked', 'access_denied', 'signing_mismatch', 'damaged', 'unavailable']);
133
- if (!statuses.has(row.status))
134
- fail('credential_readiness_status_invalid');
135
- if (row.reason_code !== null && !safeReason(row.reason_code))
136
- fail('credential_readiness_reason_invalid');
137
- if ((row.status === 'ready') !== (row.reason_code === null))
138
- fail('credential_readiness_reason_mismatch');
139
- }
140
- function validateCatalogSnapshot(value) {
141
- const row = objectRecord(value, 'catalog_snapshot_invalid');
142
- exactKeys(row, ['schema', 'version', 'models', 'checked_at'], 'catalog_snapshot_unknown_field');
143
- if (row.schema !== 'sks.catalog-snapshot.v1' || !safeVersion(row.version))
144
- fail('catalog_snapshot_invalid');
145
- const models = stringArray(row.models, 'catalog_models_invalid');
146
- if (models.some((model) => !model.trim()) || models.length !== new Set(models).size)
147
- fail('catalog_models_invalid');
148
- if (typeof row.checked_at !== 'string' || !Number.isFinite(Date.parse(row.checked_at)))
149
- fail('catalog_checked_at_invalid');
150
- }
151
- function validateFeatureCompatibility(value) {
152
- const row = objectRecord(value, 'feature_compatibility_invalid');
153
- exactKeys(row, ['schema', 'feature', 'route', 'oauth_required', 'reason_code'], 'feature_compatibility_unknown_field');
154
- if (row.schema !== 'sks.feature-compatibility.v1')
155
- fail('feature_compatibility_schema_invalid');
156
- if (!safeVersion(row.feature))
157
- fail('feature_compatibility_feature_invalid');
158
- if (!new Set(['direct-proxy', 'oauth-auxiliary', 'unavailable']).has(String(row.route)))
159
- fail('feature_compatibility_route_invalid');
160
- if (typeof row.oauth_required !== 'boolean')
161
- fail('feature_compatibility_oauth_invalid');
162
- if (row.reason_code !== null && !safeReason(row.reason_code))
163
- fail('feature_compatibility_reason_invalid');
164
- }
165
- function objectRecord(value, code) {
166
- if (!value || typeof value !== 'object' || Array.isArray(value))
167
- fail(code);
168
- return value;
169
- }
170
- function exactKeys(row, allowed, code) {
171
- const known = new Set(allowed);
172
- if (Object.keys(row).some((key) => !known.has(key)))
173
- fail(code);
174
- if (allowed.some((key) => !(key in row)))
175
- fail(code);
176
- }
177
- function stringArray(value, code) {
178
- if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string'))
179
- fail(code);
180
- return value;
181
- }
182
- function safeReason(value) {
183
- return typeof value === 'string' && /^[a-z][a-z0-9_]{2,99}$/.test(value);
184
- }
185
- function safeVersion(value) {
186
- return typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(value);
184
+ function isSensitiveAuditField(key) {
185
+ const normalized = key.trim()
186
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
187
+ .toLowerCase()
188
+ .replace(/[^a-z0-9]+/g, '_')
189
+ .replace(/^_+|_+$/g, '');
190
+ if (PROHIBITED_AUDIT_FIELDS.has(normalized))
191
+ return true;
192
+ return [...PROHIBITED_AUDIT_FIELDS].some((field) => normalized.endsWith(`_${field}`));
193
+ }
194
+ function validDecodeContext(context) {
195
+ return PROVIDERS.has(String(context.current_provider_id))
196
+ && normalizeBridgeUpstreamModelId(context.current_upstream_model) === context.current_upstream_model
197
+ && safeGeneration(context.current_catalog_generation)
198
+ && safeGeneration(context.current_route_policy_generation)
199
+ && safeHash(context.expected_child_policy_hash)
200
+ && validIso(context.created_at);
201
+ }
202
+ function historicalProvider(value) {
203
+ return value === 'codex-lb' || value === 'openrouter' ? value : null;
204
+ }
205
+ function credentialClassForProvider(provider) {
206
+ return provider === 'codex-lb' ? 'codex-lb-api-key' : 'openrouter-api-key';
207
+ }
208
+ function canonicalModelArray(value) {
209
+ if (!Array.isArray(value) || value.length > 10_000)
210
+ return null;
211
+ const models = [];
212
+ for (const entry of value) {
213
+ const model = canonicalizeBridgeModelId(entry);
214
+ if (!model || model !== entry || models.includes(model))
215
+ return null;
216
+ models.push(model);
217
+ }
218
+ return models;
219
+ }
220
+ function record(value) {
221
+ return value && typeof value === 'object' && !Array.isArray(value)
222
+ ? value
223
+ : null;
224
+ }
225
+ function hasExactKeys(row, expected) {
226
+ const keys = Object.keys(row);
227
+ const allowed = new Set(expected);
228
+ return keys.length === expected.length
229
+ && keys.every((key) => allowed.has(key))
230
+ && expected.every((key) => Object.prototype.hasOwnProperty.call(row, key));
231
+ }
232
+ function safeIdentifier(value) {
233
+ if (typeof value !== 'string')
234
+ return null;
235
+ const text = value.trim();
236
+ return text && text.length <= 240 && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(text) ? text : null;
237
+ }
238
+ function safeGeneration(value) {
239
+ return typeof value === 'string'
240
+ && value.length > 0
241
+ && value.length <= 240
242
+ && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value);
187
243
  }
188
244
  function safeHash(value) {
189
245
  return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
190
246
  }
247
+ function validIso(value) {
248
+ return typeof value === 'string'
249
+ && Number.isFinite(Date.parse(value))
250
+ && new Date(value).toISOString() === value;
251
+ }
252
+ function decodeFailure(blocker) {
253
+ return { ok: false, pin: null, blocker };
254
+ }
191
255
  function fail(code) {
192
256
  throw new ArchitectureContractError(code);
193
257
  }