@iislee/opencodex 2.11.0 → 2.35.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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,781 @@
1
+ import { enforceEventStructureLimits } from "./limits";
2
+ import { LabValidationError } from "./errors";
3
+ export { LabValidationError } from "./errors";
4
+ import {
5
+ ARTIFACT_CLASSES,
6
+ ARTIFACT_FILENAME_EXT,
7
+ CLAIM_POLARITIES,
8
+ CLAIM_SOURCE_KINDS,
9
+ EVIDENCE_LAYERS,
10
+ EVENT_KINDS,
11
+ EXECUTION_MODES,
12
+ INVALIDATION_REASONS,
13
+ LAB_EVENT_SCHEMA_VERSION,
14
+ MAX_INVALIDATION_TARGETS,
15
+ MAX_SANITIZED_STRING_FIELD,
16
+ MAX_SERIALIZED_EVENT_BYTES,
17
+ OBSERVATION_LIMIT_NAMES,
18
+ OUTCOMES,
19
+ PURGE_ACTIONS,
20
+ type ArtifactClass,
21
+ type ClaimSourceKind,
22
+ type EvidenceLayer,
23
+ type LabEventKind,
24
+ } from "../constants";
25
+ import {
26
+ claimSourceManifestDigest,
27
+ eventIdForPayload,
28
+ isSha256Hex,
29
+ jcsStringify,
30
+ subjectIdForSubject,
31
+ } from "../digest";
32
+ import { FAILURE_CLASSIFICATIONS } from "../conformance/types";
33
+ import type {
34
+ ArtifactRefV1,
35
+ ClaimCapabilityFactsV1,
36
+ ClaimSnapshotEvent,
37
+ ClaimSourceManifestV1,
38
+ ClaimSourceV1,
39
+ EvidenceSubjectV1,
40
+ InvalidationEvent,
41
+ LabEvent,
42
+ ObservationEvent,
43
+ ProtocolSubjectV1,
44
+ PurgeTombstoneEvent,
45
+ RouteCapabilityEvidenceV1,
46
+ RouteSubjectV1,
47
+ TaskSubjectV1,
48
+ } from "./types";
49
+
50
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
51
+ return !!value && typeof value === "object" && !Array.isArray(value);
52
+ }
53
+
54
+ function assertString(value: unknown, field: string, max = MAX_SANITIZED_STRING_FIELD): string {
55
+ if (typeof value !== "string") throw new LabValidationError("invalid_type", `${field} must be string`);
56
+ const bytes = new TextEncoder().encode(value).byteLength;
57
+ if (bytes > max) throw new LabValidationError("field_too_large", `${field} exceeds ${max} bytes`);
58
+ if (value.includes("\0")) throw new LabValidationError("nul_forbidden", `${field} contains NUL`);
59
+ return value;
60
+ }
61
+
62
+ function assertIntMs(value: unknown, field: string): number {
63
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
64
+ throw new LabValidationError("invalid_timestamp", `${field} must be UTC epoch milliseconds`);
65
+ }
66
+ return value;
67
+ }
68
+
69
+ function assertClosed<T extends string>(value: unknown, field: string, allowed: readonly T[]): T {
70
+ if (typeof value !== "string" || !(allowed as readonly string[]).includes(value)) {
71
+ throw new LabValidationError("closed_set", `${field} must be one of ${allowed.join("|")}`);
72
+ }
73
+ return value as T;
74
+ }
75
+
76
+ function utf8LexLess(a: string, b: string): boolean {
77
+ const ae = new TextEncoder().encode(a);
78
+ const be = new TextEncoder().encode(b);
79
+ const n = Math.min(ae.length, be.length);
80
+ for (let i = 0; i < n; i++) {
81
+ if (ae[i]! < be[i]!) return true;
82
+ if (ae[i]! > be[i]!) return false;
83
+ }
84
+ return ae.length < be.length;
85
+ }
86
+
87
+ /** Sorted unique lowercase SHA-256 hex IDs; rejects duplicates / unsorted / uppercase. */
88
+ export function validateSortedUniqueHexIds(
89
+ ids: unknown,
90
+ field: string,
91
+ opts: { nonEmpty?: boolean; max?: number } = {},
92
+ ): string[] {
93
+ if (!Array.isArray(ids)) throw new LabValidationError("invalid_type", `${field} must be array`);
94
+ if (opts.nonEmpty && ids.length === 0) {
95
+ throw new LabValidationError("empty_targets", `${field} must be non-empty`);
96
+ }
97
+ if (opts.max !== undefined && ids.length > opts.max) {
98
+ throw new LabValidationError("too_many_targets", `${field} exceeds ${opts.max}`);
99
+ }
100
+ const out: string[] = [];
101
+ for (let i = 0; i < ids.length; i++) {
102
+ const id = ids[i];
103
+ if (typeof id !== "string" || !isSha256Hex(id)) {
104
+ throw new LabValidationError("invalid_id", `${field}[${i}] must be lowercase sha256 hex`);
105
+ }
106
+ if (i > 0) {
107
+ const prev = out[i - 1]!;
108
+ if (id === prev) throw new LabValidationError("duplicate_id", `${field} contains duplicates`);
109
+ if (!utf8LexLess(prev, id)) {
110
+ throw new LabValidationError("unsorted_ids", `${field} must be UTF-8 lexicographically sorted`);
111
+ }
112
+ }
113
+ out.push(id);
114
+ }
115
+ return out;
116
+ }
117
+
118
+ function validateArtifactRef(raw: unknown, index: number): ArtifactRefV1 {
119
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_artifact_ref", `artifactRefs[${index}]`);
120
+ const digest = assertString(raw.digest, `artifactRefs[${index}].digest`);
121
+ if (!isSha256Hex(digest)) throw new LabValidationError("invalid_digest", `artifactRefs[${index}].digest`);
122
+ const mediaType = assertString(raw.mediaType, `artifactRefs[${index}].mediaType`, 256);
123
+ if (typeof raw.byteCount !== "number" || !Number.isInteger(raw.byteCount) || raw.byteCount < 0) {
124
+ throw new LabValidationError("invalid_byte_count", `artifactRefs[${index}].byteCount`);
125
+ }
126
+ const redactionPolicy = assertString(raw.redactionPolicy, `artifactRefs[${index}].redactionPolicy`, 256);
127
+ const relativePath = assertString(raw.relativePath, `artifactRefs[${index}].relativePath`, 128);
128
+ if (relativePath !== `${digest}${ARTIFACT_FILENAME_EXT}`) {
129
+ throw new LabValidationError("invalid_relative_path", `artifactRefs[${index}].relativePath must be digest-derived`);
130
+ }
131
+ if (relativePath.includes("/") || relativePath.includes("\\") || relativePath.includes("..")) {
132
+ throw new LabValidationError("path_traversal", `artifactRefs[${index}].relativePath`);
133
+ }
134
+ const artifactClass = assertClosed(raw.artifactClass, `artifactRefs[${index}].artifactClass`, ARTIFACT_CLASSES);
135
+ return { digest, mediaType, byteCount: raw.byteCount, redactionPolicy, relativePath, artifactClass };
136
+ }
137
+
138
+ function validateProtocolSubject(raw: Record<string, unknown>): ProtocolSubjectV1 {
139
+ if (raw.subjectSchemaVersion !== 1 || raw.subjectKind !== "protocol") {
140
+ throw new LabValidationError("invalid_subject", "protocol subject schema/kind mismatch");
141
+ }
142
+ return {
143
+ subjectSchemaVersion: 1,
144
+ subjectKind: "protocol",
145
+ opencodexCompatibilityVersion: assertString(raw.opencodexCompatibilityVersion, "opencodexCompatibilityVersion"),
146
+ effectiveAdapter: assertString(raw.effectiveAdapter, "effectiveAdapter"),
147
+ inboundProtocol: assertString(raw.inboundProtocol, "inboundProtocol"),
148
+ upstreamProtocol: assertString(raw.upstreamProtocol, "upstreamProtocol"),
149
+ surface: assertString(raw.surface, "surface"),
150
+ behaviorFingerprint: assertString(raw.behaviorFingerprint, "behaviorFingerprint"),
151
+ };
152
+ }
153
+
154
+ function validateRouteSubject(raw: Record<string, unknown>): RouteSubjectV1 {
155
+ if (raw.subjectSchemaVersion !== 1 || raw.subjectKind !== "route") {
156
+ throw new LabValidationError("invalid_subject", "route subject schema/kind mismatch");
157
+ }
158
+ if (!Array.isArray(raw.dependencies)) {
159
+ throw new LabValidationError("invalid_subject", "dependencies must be array");
160
+ }
161
+ const dependencies = raw.dependencies.map((dep, i) => {
162
+ if (!isPlainObject(dep)) throw new LabValidationError("invalid_dependency", `dependencies[${i}]`);
163
+ return {
164
+ role: assertString(dep.role, `dependencies[${i}].role`),
165
+ providerId: assertString(dep.providerId, `dependencies[${i}].providerId`),
166
+ providerInstanceFingerprint: assertString(dep.providerInstanceFingerprint, `dependencies[${i}].providerInstanceFingerprint`),
167
+ clientModelId: assertString(dep.clientModelId, `dependencies[${i}].clientModelId`),
168
+ upstreamModelId: assertString(dep.upstreamModelId, `dependencies[${i}].upstreamModelId`),
169
+ effectiveAdapter: assertString(dep.effectiveAdapter, `dependencies[${i}].effectiveAdapter`),
170
+ upstreamProtocol: assertString(dep.upstreamProtocol, `dependencies[${i}].upstreamProtocol`),
171
+ endpointFingerprint: assertString(dep.endpointFingerprint, `dependencies[${i}].endpointFingerprint`),
172
+ behaviorFingerprint: assertString(dep.behaviorFingerprint, `dependencies[${i}].behaviorFingerprint`),
173
+ };
174
+ });
175
+ return {
176
+ subjectSchemaVersion: 1,
177
+ subjectKind: "route",
178
+ providerId: assertString(raw.providerId, "providerId"),
179
+ providerInstanceFingerprint: assertString(raw.providerInstanceFingerprint, "providerInstanceFingerprint"),
180
+ clientModelId: assertString(raw.clientModelId, "clientModelId"),
181
+ upstreamModelId: assertString(raw.upstreamModelId, "upstreamModelId"),
182
+ effectiveAdapter: assertString(raw.effectiveAdapter, "effectiveAdapter"),
183
+ inboundProtocol: assertString(raw.inboundProtocol, "inboundProtocol"),
184
+ upstreamProtocol: assertString(raw.upstreamProtocol, "upstreamProtocol"),
185
+ surface: assertString(raw.surface, "surface"),
186
+ opencodexCompatibilityVersion: assertString(raw.opencodexCompatibilityVersion, "opencodexCompatibilityVersion"),
187
+ behaviorFingerprint: assertString(raw.behaviorFingerprint, "behaviorFingerprint"),
188
+ endpointFingerprint: assertString(raw.endpointFingerprint, "endpointFingerprint"),
189
+ dependencies,
190
+ };
191
+ }
192
+
193
+ function validateTaskSubject(raw: Record<string, unknown>): TaskSubjectV1 {
194
+ if (raw.subjectSchemaVersion !== 1 || raw.subjectKind !== "task") {
195
+ throw new LabValidationError("invalid_subject", "task subject schema/kind mismatch");
196
+ }
197
+ if (!isPlainObject(raw.routeSubject)) {
198
+ throw new LabValidationError("invalid_subject", "task.routeSubject required");
199
+ }
200
+ return {
201
+ subjectSchemaVersion: 1,
202
+ subjectKind: "task",
203
+ routeSubject: validateRouteSubject(raw.routeSubject),
204
+ taskClassId: assertString(raw.taskClassId, "taskClassId"),
205
+ taskClassVersion: assertString(raw.taskClassVersion, "taskClassVersion"),
206
+ taskFixtureDigest: assertString(raw.taskFixtureDigest, "taskFixtureDigest"),
207
+ verifierManifestDigest: assertString(raw.verifierManifestDigest, "verifierManifestDigest"),
208
+ fabricCompatibilityVersion: assertString(raw.fabricCompatibilityVersion, "fabricCompatibilityVersion"),
209
+ sandboxProfileDigest: assertString(raw.sandboxProfileDigest, "sandboxProfileDigest"),
210
+ };
211
+ }
212
+
213
+ export function validateSubject(raw: unknown, layer: EvidenceLayer): EvidenceSubjectV1 {
214
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_subject", "subject must be object");
215
+ const kind = raw.subjectKind;
216
+ if (layer === "protocol_conformance") {
217
+ if (kind !== "protocol") throw new LabValidationError("layer_subject_mismatch", "protocol layer requires protocol subject");
218
+ return validateProtocolSubject(raw);
219
+ }
220
+ if (layer === "live_route_compatibility") {
221
+ if (kind !== "route") throw new LabValidationError("layer_subject_mismatch", "live layer requires route subject");
222
+ return validateRouteSubject(raw);
223
+ }
224
+ if (layer === "task_effectiveness") {
225
+ if (kind !== "task") throw new LabValidationError("layer_subject_mismatch", "task layer requires task subject");
226
+ return validateTaskSubject(raw);
227
+ }
228
+ const _exhaustive: never = layer;
229
+ throw new LabValidationError("unknown_layer", String(_exhaustive));
230
+ }
231
+
232
+ function stripEventId(event: Record<string, unknown>): Record<string, unknown> {
233
+ const { eventId: _omit, ...rest } = event;
234
+ return rest;
235
+ }
236
+
237
+ function enforceEventId(event: LabEvent): void {
238
+ const recomputed = eventIdForPayload(stripEventId(event as unknown as Record<string, unknown>));
239
+ if (event.eventId !== recomputed) {
240
+ throw new LabValidationError("event_id_mismatch", `eventId mismatch: got ${event.eventId}, expected ${recomputed}`);
241
+ }
242
+ }
243
+
244
+ function enforceSerializedSize(event: LabEvent): void {
245
+ const bytes = new TextEncoder().encode(jcsStringify(event)).byteLength;
246
+ if (bytes > MAX_SERIALIZED_EVENT_BYTES) {
247
+ throw new LabValidationError("event_too_large", `serialized event exceeds ${MAX_SERIALIZED_EVENT_BYTES} bytes`);
248
+ }
249
+ }
250
+
251
+ function validateAssertionRecord(raw: unknown, index: number): ObservationEvent["assertions"][number] {
252
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_assertions", `assertions[${index}]`);
253
+ const allowed = new Set(["id", "operator", "required", "passed", "expectedSummary", "observedSummary", "reason"]);
254
+ for (const key of Object.keys(raw)) {
255
+ if (!allowed.has(key)) {
256
+ throw new LabValidationError("unknown_assertion_key", `assertions[${index}].${key}`);
257
+ }
258
+ }
259
+ const record = {
260
+ id: assertString(raw.id, `assertions[${index}].id`),
261
+ operator: assertString(raw.operator, `assertions[${index}].operator`),
262
+ required: raw.required === true || raw.required === false
263
+ ? raw.required
264
+ : (() => { throw new LabValidationError("invalid_assertions", `assertions[${index}].required`); })(),
265
+ passed: raw.passed === true || raw.passed === false
266
+ ? raw.passed
267
+ : (() => { throw new LabValidationError("invalid_assertions", `assertions[${index}].passed`); })(),
268
+ expectedSummary: assertString(raw.expectedSummary, `assertions[${index}].expectedSummary`),
269
+ observedSummary: assertString(raw.observedSummary, `assertions[${index}].observedSummary`),
270
+ ...(raw.reason !== undefined ? { reason: assertString(raw.reason, `assertions[${index}].reason`) } : {}),
271
+ };
272
+ return record;
273
+ }
274
+
275
+ function validateObservationLimits(raw: unknown): Record<string, number | null> {
276
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_limits", "limits");
277
+ const out: Record<string, number | null> = {};
278
+ for (const key of Object.keys(raw)) {
279
+ if (!(OBSERVATION_LIMIT_NAMES as readonly string[]).includes(key)) {
280
+ throw new LabValidationError("unknown_limit", `limits.${key}`);
281
+ }
282
+ const value = raw[key];
283
+ if (value === null) {
284
+ out[key] = null;
285
+ continue;
286
+ }
287
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
288
+ throw new LabValidationError("invalid_limits", `limits.${key}`);
289
+ }
290
+ out[key] = value;
291
+ }
292
+ return out;
293
+ }
294
+
295
+ function validateObservationEnvironment(raw: unknown): Record<string, unknown> {
296
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_environment", "environment");
297
+ const topKeys = Object.keys(raw);
298
+ if (topKeys.length !== 1 || topKeys[0] !== "runtime") {
299
+ throw new LabValidationError("invalid_environment", "environment must contain only runtime");
300
+ }
301
+ const runtime = raw.runtime;
302
+ if (!isPlainObject(runtime)) throw new LabValidationError("invalid_environment", "environment.runtime");
303
+ const runtimeKeys = Object.keys(runtime);
304
+ const allowedRuntime = new Set(["platform", "arch", "bunVersion"]);
305
+ for (const key of runtimeKeys) {
306
+ if (!allowedRuntime.has(key)) {
307
+ throw new LabValidationError("unknown_environment_key", `environment.runtime.${key}`);
308
+ }
309
+ }
310
+ const out: Record<string, unknown> = {
311
+ runtime: {
312
+ platform: assertString(runtime.platform, "environment.runtime.platform"),
313
+ arch: assertString(runtime.arch, "environment.runtime.arch"),
314
+ ...(runtime.bunVersion !== undefined
315
+ ? { bunVersion: assertString(runtime.bunVersion, "environment.runtime.bunVersion") }
316
+ : {}),
317
+ },
318
+ };
319
+ return out;
320
+ }
321
+
322
+ function validateExpectedFailure(raw: unknown): Record<string, unknown> {
323
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_expected_failure", "expectedFailure");
324
+ const allowed = new Set([
325
+ "controlKind",
326
+ "expectedClass",
327
+ "expectedCode",
328
+ "assertionIds",
329
+ "onMatch",
330
+ "onMismatch",
331
+ ]);
332
+ for (const key of Object.keys(raw)) {
333
+ if (!allowed.has(key)) {
334
+ throw new LabValidationError("unknown_expected_failure_key", `expectedFailure.${key}`);
335
+ }
336
+ }
337
+ if (!Array.isArray(raw.assertionIds) || !raw.assertionIds.every((id) => typeof id === "string")) {
338
+ throw new LabValidationError("invalid_expected_failure", "expectedFailure.assertionIds");
339
+ }
340
+ return {
341
+ controlKind: assertClosed(raw.controlKind, "expectedFailure.controlKind", [
342
+ "conformance_negative_control",
343
+ "capability_absence_control",
344
+ ] as const),
345
+ expectedClass: assertString(raw.expectedClass, "expectedFailure.expectedClass"),
346
+ expectedCode: assertString(raw.expectedCode, "expectedFailure.expectedCode"),
347
+ assertionIds: raw.assertionIds as string[],
348
+ onMatch: assertClosed(raw.onMatch, "expectedFailure.onMatch", ["pass", "unsupported"] as const),
349
+ onMismatch: assertClosed(raw.onMismatch, "expectedFailure.onMismatch", ["fail", "inconclusive"] as const),
350
+ };
351
+ }
352
+
353
+ function validateSourceRefs(raw: unknown): string[] {
354
+ if (!Array.isArray(raw)) throw new LabValidationError("invalid_source_refs", "sourceRefs");
355
+ return raw.map((r, i) => assertString(r, `sourceRefs[${i}]`));
356
+ }
357
+
358
+ function validateObservation(raw: Record<string, unknown>): ObservationEvent {
359
+ const evidenceLayer = assertClosed(raw.evidenceLayer, "evidenceLayer", EVIDENCE_LAYERS);
360
+ const subject = validateSubject(raw.subject, evidenceLayer);
361
+ const subjectId = assertString(raw.subjectId, "subjectId");
362
+ if (subjectId !== subjectIdForSubject(subject)) {
363
+ throw new LabValidationError("subject_id_mismatch", "subjectId does not match subject");
364
+ }
365
+ if (!Array.isArray(raw.fixtureDigests) || raw.fixtureDigests.length === 0) {
366
+ throw new LabValidationError("missing_fixture_digests", "fixtureDigests required");
367
+ }
368
+ const fixtureDigests = raw.fixtureDigests.map((d, i) => {
369
+ const digest = assertString(d, `fixtureDigests[${i}]`);
370
+ if (!isSha256Hex(digest)) throw new LabValidationError("invalid_digest", `fixtureDigests[${i}]`);
371
+ return digest;
372
+ });
373
+ const scenarioManifestDigest = assertString(raw.scenarioManifestDigest, "scenarioManifestDigest");
374
+ const suiteManifestDigest = assertString(raw.suiteManifestDigest, "suiteManifestDigest");
375
+ if (!isSha256Hex(scenarioManifestDigest) || !isSha256Hex(suiteManifestDigest)) {
376
+ throw new LabValidationError("invalid_digest", "scenario/suite manifest digest");
377
+ }
378
+ if (!Array.isArray(raw.assertions)) throw new LabValidationError("invalid_assertions", "assertions");
379
+ if (!Array.isArray(raw.artifactRefs)) throw new LabValidationError("invalid_artifact_refs", "artifactRefs");
380
+ const event: ObservationEvent = {
381
+ schemaVersion: LAB_EVENT_SCHEMA_VERSION,
382
+ eventId: assertString(raw.eventId, "eventId"),
383
+ eventKind: "observation",
384
+ recordedAt: assertIntMs(raw.recordedAt, "recordedAt"),
385
+ producer: assertString(raw.producer, "producer"),
386
+ producerVersion: assertString(raw.producerVersion, "producerVersion"),
387
+ evidenceLayer,
388
+ scenarioId: assertString(raw.scenarioId, "scenarioId"),
389
+ scenarioVersion: assertString(raw.scenarioVersion, "scenarioVersion"),
390
+ scenarioManifestDigest,
391
+ suiteId: assertString(raw.suiteId, "suiteId"),
392
+ suiteVersion: assertString(raw.suiteVersion, "suiteVersion"),
393
+ suiteManifestDigest,
394
+ fixtureDigests,
395
+ subject,
396
+ subjectId,
397
+ startedAt: assertIntMs(raw.startedAt, "startedAt"),
398
+ completedAt: assertIntMs(raw.completedAt, "completedAt"),
399
+ executionMode: assertClosed(raw.executionMode, "executionMode", EXECUTION_MODES),
400
+ attempt: typeof raw.attempt === "number" && Number.isInteger(raw.attempt) && raw.attempt >= 1
401
+ ? raw.attempt
402
+ : (() => { throw new LabValidationError("invalid_attempt", "attempt"); })(),
403
+ limits: validateObservationLimits(raw.limits),
404
+ outcome: assertClosed(raw.outcome, "outcome", OUTCOMES),
405
+ assertions: raw.assertions.map(validateAssertionRecord),
406
+ environment: validateObservationEnvironment(raw.environment),
407
+ artifactRefs: raw.artifactRefs.map(validateArtifactRef),
408
+ };
409
+ if (raw.failure !== undefined) {
410
+ if (!isPlainObject(raw.failure)) throw new LabValidationError("invalid_failure", "failure");
411
+ event.failure = {
412
+ class: assertClosed(raw.failure.class, "failure.class", FAILURE_CLASSIFICATIONS),
413
+ code: assertString(raw.failure.code, "failure.code"),
414
+ retryable: raw.failure.retryable === true,
415
+ attribution: assertClosed(raw.failure.attribution, "failure.attribution", [
416
+ "opencodex",
417
+ "route",
418
+ "environment",
419
+ "harness",
420
+ ] as const),
421
+ };
422
+ }
423
+ if (raw.expectedFailure !== undefined) {
424
+ event.expectedFailure = validateExpectedFailure(raw.expectedFailure);
425
+ }
426
+ if (raw.sourceRefs !== undefined) {
427
+ event.sourceRefs = validateSourceRefs(raw.sourceRefs);
428
+ }
429
+ if (event.completedAt < event.startedAt) {
430
+ throw new LabValidationError("invalid_time_range", "completedAt < startedAt");
431
+ }
432
+ return event;
433
+ }
434
+
435
+ function validateClaimSnapshot(raw: Record<string, unknown>): ClaimSnapshotEvent {
436
+ if (raw.evidenceLayer !== "live_route_compatibility") {
437
+ throw new LabValidationError("invalid_claim_layer", "claims exist only for live_route_compatibility");
438
+ }
439
+ const subject = validateSubject(raw.subject, "live_route_compatibility") as RouteSubjectV1;
440
+ const subjectId = assertString(raw.subjectId, "subjectId");
441
+ if (subjectId !== subjectIdForSubject(subject)) {
442
+ throw new LabValidationError("subject_id_mismatch", "subjectId does not match subject");
443
+ }
444
+ const sourceManifestDigest = assertString(raw.sourceManifestDigest, "sourceManifestDigest");
445
+ if (!isSha256Hex(sourceManifestDigest)) {
446
+ throw new LabValidationError("invalid_digest", "sourceManifestDigest");
447
+ }
448
+ return {
449
+ schemaVersion: LAB_EVENT_SCHEMA_VERSION,
450
+ eventId: assertString(raw.eventId, "eventId"),
451
+ eventKind: "claim_snapshot",
452
+ recordedAt: assertIntMs(raw.recordedAt, "recordedAt"),
453
+ producer: assertString(raw.producer, "producer"),
454
+ producerVersion: assertString(raw.producerVersion, "producerVersion"),
455
+ evidenceLayer: "live_route_compatibility",
456
+ subject,
457
+ subjectId,
458
+ capability: assertString(raw.capability, "capability"),
459
+ polarity: assertClosed(raw.polarity, "polarity", CLAIM_POLARITIES),
460
+ sourceManifestDigest,
461
+ sourceEventIds: validateSortedUniqueHexIds(raw.sourceEventIds, "sourceEventIds", {
462
+ nonEmpty: false,
463
+ max: MAX_INVALIDATION_TARGETS,
464
+ }),
465
+ supersedes: validateSortedUniqueHexIds(raw.supersedes, "supersedes", {
466
+ nonEmpty: false,
467
+ max: MAX_INVALIDATION_TARGETS,
468
+ }),
469
+ effectiveAt: assertIntMs(raw.effectiveAt, "effectiveAt"),
470
+ };
471
+ }
472
+
473
+ function validateInvalidation(raw: Record<string, unknown>): InvalidationEvent {
474
+ return {
475
+ schemaVersion: LAB_EVENT_SCHEMA_VERSION,
476
+ eventId: assertString(raw.eventId, "eventId"),
477
+ eventKind: "invalidation",
478
+ recordedAt: assertIntMs(raw.recordedAt, "recordedAt"),
479
+ producer: assertString(raw.producer, "producer"),
480
+ producerVersion: assertString(raw.producerVersion, "producerVersion"),
481
+ targetEventIds: validateSortedUniqueHexIds(raw.targetEventIds, "targetEventIds", {
482
+ nonEmpty: true,
483
+ max: MAX_INVALIDATION_TARGETS,
484
+ }),
485
+ reason: assertClosed(raw.reason, "reason", INVALIDATION_REASONS),
486
+ };
487
+ }
488
+
489
+ function validatePurge(raw: Record<string, unknown>): PurgeTombstoneEvent {
490
+ const targetEventIds = validateSortedUniqueHexIds(raw.targetEventIds ?? [], "targetEventIds", {
491
+ nonEmpty: false,
492
+ max: MAX_INVALIDATION_TARGETS,
493
+ });
494
+ const targetArtifactDigests = validateSortedUniqueHexIds(
495
+ raw.targetArtifactDigests ?? [],
496
+ "targetArtifactDigests",
497
+ { nonEmpty: false, max: MAX_INVALIDATION_TARGETS },
498
+ );
499
+ if (raw.reason !== "sensitive_evidence") {
500
+ throw new LabValidationError("invalid_purge_reason", "reason must be sensitive_evidence");
501
+ }
502
+ if (!Array.isArray(raw.purgeActions) || raw.purgeActions.length === 0) {
503
+ throw new LabValidationError("empty_purge_actions", "purgeActions required");
504
+ }
505
+ const purgeActions = raw.purgeActions.map((a, i) =>
506
+ assertClosed(a, `purgeActions[${i}]`, PURGE_ACTIONS),
507
+ );
508
+ for (let i = 0; i < purgeActions.length; i++) {
509
+ for (let j = i + 1; j < purgeActions.length; j++) {
510
+ if (purgeActions[i] === purgeActions[j]) {
511
+ throw new LabValidationError("duplicate_purge_action", "purgeActions must be unique");
512
+ }
513
+ }
514
+ }
515
+ const sortedActions = [...purgeActions].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
516
+ for (let i = 0; i < purgeActions.length; i++) {
517
+ if (purgeActions[i] !== sortedActions[i]) {
518
+ throw new LabValidationError("unsorted_purge_actions", "purgeActions must be sorted");
519
+ }
520
+ }
521
+ // Directory-scoped actions (scratch/export) are meaningful without event or
522
+ // artifact ids — default sensitive purge wipes those trees and still records
523
+ // a tombstone. Ledger/sqlite/artifact-only tombstones still need a target.
524
+ const hasDirectoryPurge = purgeActions.includes("scratch") || purgeActions.includes("export");
525
+ if (
526
+ targetEventIds.length === 0
527
+ && targetArtifactDigests.length === 0
528
+ && !hasDirectoryPurge
529
+ ) {
530
+ throw new LabValidationError("empty_purge_targets", "at least one purge target required");
531
+ }
532
+ return {
533
+ schemaVersion: LAB_EVENT_SCHEMA_VERSION,
534
+ eventId: assertString(raw.eventId, "eventId"),
535
+ eventKind: "purge_tombstone",
536
+ recordedAt: assertIntMs(raw.recordedAt, "recordedAt"),
537
+ producer: assertString(raw.producer, "producer"),
538
+ producerVersion: assertString(raw.producerVersion, "producerVersion"),
539
+ targetEventIds,
540
+ targetArtifactDigests,
541
+ reason: "sensitive_evidence",
542
+ purgeActions,
543
+ };
544
+ }
545
+
546
+ /** Structural validation of a parsed JSON object into a LabEvent (fail-closed). */
547
+ export function validateLabEvent(raw: unknown): LabEvent {
548
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_event", "event must be object");
549
+ if (raw.schemaVersion !== LAB_EVENT_SCHEMA_VERSION) {
550
+ throw new LabValidationError("schema_version", "unsupported schemaVersion");
551
+ }
552
+ const kind = assertClosed(raw.eventKind, "eventKind", EVENT_KINDS) as LabEventKind;
553
+ let event: LabEvent;
554
+ switch (kind) {
555
+ case "observation":
556
+ event = validateObservation(raw);
557
+ break;
558
+ case "claim_snapshot":
559
+ event = validateClaimSnapshot(raw);
560
+ break;
561
+ case "invalidation":
562
+ event = validateInvalidation(raw);
563
+ break;
564
+ case "purge_tombstone":
565
+ event = validatePurge(raw);
566
+ break;
567
+ default: {
568
+ const _never: never = kind;
569
+ throw new LabValidationError("unknown_kind", String(_never));
570
+ }
571
+ }
572
+ enforceEventId(event);
573
+ enforceEventStructureLimits(event);
574
+ enforceSerializedSize(event);
575
+ return event;
576
+ }
577
+
578
+ const FORBIDDEN_FACT_KEYS = new Set([
579
+ "baseUrl",
580
+ "hostname",
581
+ "url",
582
+ "headers",
583
+ "authorization",
584
+ "apiKey",
585
+ "credential",
586
+ "account",
587
+ "email",
588
+ "path",
589
+ "prompt",
590
+ "repository",
591
+ ]);
592
+
593
+ const ALLOWED_FACT_KEYS = new Set([
594
+ "contextWindow",
595
+ "inputModalities",
596
+ "reasoningEfforts",
597
+ "catalogCapabilityNames",
598
+ "serviceTier",
599
+ "toolCapable",
600
+ "parallelToolCalls",
601
+ "endpointLocality",
602
+ "canonicalOpenAiForward",
603
+ ]);
604
+
605
+ function validateFacts(raw: unknown, label: string): ClaimCapabilityFactsV1 {
606
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_facts", label);
607
+ for (const key of Object.keys(raw)) {
608
+ if (FORBIDDEN_FACT_KEYS.has(key) || /url|host|header|secret|token|cookie|email|path/i.test(key)) {
609
+ throw new LabValidationError("forbidden_fact", `${label}.${key} forbidden`);
610
+ }
611
+ if (!ALLOWED_FACT_KEYS.has(key)) {
612
+ throw new LabValidationError("unknown_fact", `${label}.${key} unknown`);
613
+ }
614
+ }
615
+ const facts: ClaimCapabilityFactsV1 = {};
616
+ if (raw.contextWindow !== undefined) {
617
+ if (typeof raw.contextWindow !== "number" || !Number.isInteger(raw.contextWindow) || raw.contextWindow < 0) {
618
+ throw new LabValidationError("invalid_facts", "contextWindow");
619
+ }
620
+ facts.contextWindow = raw.contextWindow;
621
+ }
622
+ if (raw.inputModalities !== undefined) {
623
+ if (!Array.isArray(raw.inputModalities) || !raw.inputModalities.every((x) => typeof x === "string")) {
624
+ throw new LabValidationError("invalid_facts", "inputModalities");
625
+ }
626
+ facts.inputModalities = raw.inputModalities as string[];
627
+ }
628
+ if (raw.reasoningEfforts !== undefined) {
629
+ if (!Array.isArray(raw.reasoningEfforts) || !raw.reasoningEfforts.every((x) => typeof x === "string")) {
630
+ throw new LabValidationError("invalid_facts", "reasoningEfforts");
631
+ }
632
+ facts.reasoningEfforts = raw.reasoningEfforts as string[];
633
+ }
634
+ if (raw.catalogCapabilityNames !== undefined) {
635
+ if (!Array.isArray(raw.catalogCapabilityNames) || !raw.catalogCapabilityNames.every((x) => typeof x === "string")) {
636
+ throw new LabValidationError("invalid_facts", "catalogCapabilityNames");
637
+ }
638
+ facts.catalogCapabilityNames = raw.catalogCapabilityNames as string[];
639
+ }
640
+ if (raw.serviceTier !== undefined) {
641
+ if (!Array.isArray(raw.serviceTier) || !raw.serviceTier.every((x) => typeof x === "string")) {
642
+ throw new LabValidationError("invalid_facts", "serviceTier");
643
+ }
644
+ facts.serviceTier = raw.serviceTier as string[];
645
+ }
646
+ if (raw.toolCapable !== undefined) {
647
+ if (typeof raw.toolCapable !== "boolean") throw new LabValidationError("invalid_facts", "toolCapable");
648
+ facts.toolCapable = raw.toolCapable;
649
+ }
650
+ if (raw.parallelToolCalls !== undefined) {
651
+ if (typeof raw.parallelToolCalls !== "boolean") throw new LabValidationError("invalid_facts", "parallelToolCalls");
652
+ facts.parallelToolCalls = raw.parallelToolCalls;
653
+ }
654
+ if (raw.endpointLocality !== undefined) {
655
+ facts.endpointLocality = assertClosed(raw.endpointLocality, "endpointLocality", [
656
+ "local",
657
+ "private",
658
+ "unknown",
659
+ ] as const);
660
+ }
661
+ if (raw.canonicalOpenAiForward !== undefined) {
662
+ if (typeof raw.canonicalOpenAiForward !== "boolean") {
663
+ throw new LabValidationError("invalid_facts", "canonicalOpenAiForward");
664
+ }
665
+ facts.canonicalOpenAiForward = raw.canonicalOpenAiForward;
666
+ }
667
+ return facts;
668
+ }
669
+
670
+ function validateResolvedEvidence(raw: unknown): RouteCapabilityEvidenceV1 {
671
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_resolved", "resolvedEvidence");
672
+ const out: RouteCapabilityEvidenceV1 = {};
673
+ const copyBoolOrUnknown = (key: keyof RouteCapabilityEvidenceV1) => {
674
+ const v = raw[key as string];
675
+ if (v === undefined) return;
676
+ if (v !== true && v !== false && v !== "unknown") {
677
+ throw new LabValidationError("invalid_resolved", String(key));
678
+ }
679
+ (out as Record<string, unknown>)[key] = v;
680
+ };
681
+ if (raw.contextWindow !== undefined) {
682
+ if (typeof raw.contextWindow !== "number" || !Number.isInteger(raw.contextWindow)) {
683
+ throw new LabValidationError("invalid_resolved", "contextWindow");
684
+ }
685
+ out.contextWindow = raw.contextWindow;
686
+ }
687
+ copyBoolOrUnknown("tools");
688
+ copyBoolOrUnknown("image");
689
+ copyBoolOrUnknown("structuredOutput");
690
+ copyBoolOrUnknown("localOnly");
691
+ copyBoolOrUnknown("remoteAllowed");
692
+ copyBoolOrUnknown("encryptedCodexTasks");
693
+ if (raw.reasoningEfforts !== undefined) {
694
+ if (!Array.isArray(raw.reasoningEfforts) || !raw.reasoningEfforts.every((x) => typeof x === "string")) {
695
+ throw new LabValidationError("invalid_resolved", "reasoningEfforts");
696
+ }
697
+ out.reasoningEfforts = raw.reasoningEfforts as string[];
698
+ }
699
+ if (raw.serviceTier !== undefined) {
700
+ if (typeof raw.serviceTier !== "string") throw new LabValidationError("invalid_resolved", "serviceTier");
701
+ out.serviceTier = raw.serviceTier;
702
+ }
703
+ return out;
704
+ }
705
+
706
+ /** Validate ClaimSourceManifestV1 and return canonical object + digest. */
707
+ export function validateClaimSourceManifest(raw: unknown): {
708
+ manifest: ClaimSourceManifestV1;
709
+ digest: string;
710
+ } {
711
+ if (!isPlainObject(raw)) throw new LabValidationError("invalid_claim_source", "manifest must be object");
712
+ if (raw.schemaVersion !== 1) throw new LabValidationError("schema_version", "ClaimSourceManifest schemaVersion");
713
+ if (!Array.isArray(raw.sources)) throw new LabValidationError("invalid_sources", "sources");
714
+ const seen = new Set<ClaimSourceKind>();
715
+ const sources: ClaimSourceV1[] = [];
716
+ for (let i = 0; i < raw.sources.length; i++) {
717
+ const src = raw.sources[i];
718
+ if (!isPlainObject(src)) throw new LabValidationError("invalid_source", `sources[${i}]`);
719
+ const kind = assertClosed(src.kind, `sources[${i}].kind`, CLAIM_SOURCE_KINDS);
720
+ if (seen.has(kind)) throw new LabValidationError("duplicate_source_kind", kind);
721
+ seen.add(kind);
722
+ if (src.revision !== null && typeof src.revision !== "string") {
723
+ throw new LabValidationError("invalid_revision", `sources[${i}].revision`);
724
+ }
725
+ sources.push({
726
+ kind,
727
+ revision: src.revision as string | null,
728
+ facts: validateFacts(src.facts, `sources[${i}].facts`),
729
+ });
730
+ }
731
+ // Closed kind order
732
+ for (let i = 1; i < sources.length; i++) {
733
+ const prevIdx = CLAIM_SOURCE_KINDS.indexOf(sources[i - 1]!.kind);
734
+ const curIdx = CLAIM_SOURCE_KINDS.indexOf(sources[i]!.kind);
735
+ if (curIdx <= prevIdx) {
736
+ throw new LabValidationError("source_order", "sources must follow closed kind order");
737
+ }
738
+ }
739
+ const manifest: ClaimSourceManifestV1 = {
740
+ schemaVersion: 1,
741
+ subjectId: assertString(raw.subjectId, "subjectId"),
742
+ providerId: assertString(raw.providerId, "providerId"),
743
+ clientModelId: assertString(raw.clientModelId, "clientModelId"),
744
+ capability: assertString(raw.capability, "capability"),
745
+ sources,
746
+ resolvedEvidence: validateResolvedEvidence(raw.resolvedEvidence),
747
+ };
748
+ if (!isSha256Hex(manifest.subjectId)) {
749
+ throw new LabValidationError("invalid_subject_id", "subjectId must be sha256 hex");
750
+ }
751
+ return { manifest, digest: claimSourceManifestDigest(manifest) };
752
+ }
753
+
754
+ export function assignEventId<T extends Omit<LabEvent, "eventId"> & { eventId?: string }>(
755
+ eventWithoutId: T,
756
+ ): T & { eventId: string } {
757
+ const { eventId: _ignored, ...rest } = eventWithoutId as T & { eventId?: string };
758
+ const eventId = eventIdForPayload(rest);
759
+ return { ...rest, eventId } as T & { eventId: string };
760
+ }
761
+
762
+ export function artifactClassMediaType(artifactClass: ArtifactClass): string {
763
+ switch (artifactClass) {
764
+ case "scenario_manifest":
765
+ case "suite_manifest":
766
+ case "claim_source_manifest":
767
+ case "assertion_report":
768
+ case "request_shape":
769
+ case "response_shape":
770
+ case "event_trace":
771
+ case "error_taxonomy":
772
+ case "verifier_summary":
773
+ return "application/json";
774
+ case "fixture":
775
+ return "application/octet-stream";
776
+ default: {
777
+ const _never: never = artifactClass;
778
+ return _never;
779
+ }
780
+ }
781
+ }