@iislee/opencodex 2.11.0 → 2.31.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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -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 +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,586 @@
1
+ /**
2
+ * Deny-by-default sanitization before artifact hashing/writing.
3
+ * Never persists prompts, secrets, paths, account IDs, raw URLs, or provider bodies.
4
+ */
5
+ import type { ArtifactClass } from "../constants";
6
+ import { MAX_SANITIZED_STRING_FIELD } from "../constants";
7
+ import { jcsStringify } from "../digest";
8
+ import { redactSecretString } from "../../lib/redact";
9
+
10
+ const FORBIDDEN_KEY = /^(?:authorization|proxy-authorization|cookie|set-cookie|api[-_]?key|x-api-key|token|secret|password|email|prompt|messages|content|body|url|hostname|baseUrl|path|account|alias)$/i;
11
+ const SECRETISH = /sk-[a-z0-9]{10,}|credential-canary-[a-z0-9]{10,}|Bearer\s+[A-Za-z0-9._\-]+|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}/i;
12
+ const SECRETISH_GLOBAL = new RegExp(SECRETISH.source, "gi");
13
+
14
+ export function redactForArtifact(artifactClass: ArtifactClass, payload: unknown): unknown {
15
+ if (
16
+ artifactClass === "fixture" ||
17
+ artifactClass === "scenario_manifest" ||
18
+ artifactClass === "suite_manifest" ||
19
+ artifactClass === "claim_source_manifest"
20
+ ) {
21
+ // Contract artifacts are already synthetic/canonical. Mutating them would
22
+ // invalidate content-addressed digests; reject secret-shaped material instead.
23
+ assertNoSecretMaterial(payload, 0);
24
+ return payload;
25
+ }
26
+ return scrubValue(payload, 0);
27
+ }
28
+
29
+ const FORBIDDEN_CONTRACT_KEYS = /^(?:authorization|proxy-authorization|cookie|set-cookie|api[-_]?key|x-api-key|token|secret|password|email|prompt|messages|baseUrl|hostname|account|alias)$/i;
30
+
31
+ function assertNoSecretMaterial(value: unknown, depth: number): void {
32
+ if (depth > 8) {
33
+ throw new Error("contract artifact exceeds sanitization inspection depth");
34
+ }
35
+ if (typeof value === "string") {
36
+ if (SECRETISH.test(value)) {
37
+ throw new Error("contract artifact contains forbidden secret-shaped material");
38
+ }
39
+ return;
40
+ }
41
+ if (Array.isArray(value)) {
42
+ for (const item of value) assertNoSecretMaterial(item, depth + 1);
43
+ return;
44
+ }
45
+ if (value && typeof value === "object") {
46
+ for (const [key, child] of Object.entries(value as object)) {
47
+ if (FORBIDDEN_CONTRACT_KEYS.test(key)) {
48
+ throw new Error(`contract artifact forbids key ${key}`);
49
+ }
50
+ assertNoSecretMaterial(child, depth + 1);
51
+ }
52
+ }
53
+ }
54
+
55
+ function scrubValue(value: unknown, depth: number): unknown {
56
+ if (depth > 8) return "[truncated_depth]";
57
+ if (value === null || typeof value === "boolean" || typeof value === "number") return value;
58
+ if (typeof value === "string") return scrubString(value);
59
+ if (value instanceof Uint8Array) {
60
+ const text = new TextDecoder().decode(value);
61
+ return new TextEncoder().encode(scrubString(text));
62
+ }
63
+ if (Array.isArray(value)) {
64
+ if (value.length > 256) return value.slice(0, 256).map((v) => scrubValue(v, depth + 1));
65
+ return value.map((v) => scrubValue(v, depth + 1));
66
+ }
67
+ if (typeof value === "object") {
68
+ const out: Record<string, unknown> = {};
69
+ const keys = Object.keys(value as object).slice(0, 64);
70
+ for (const key of keys) {
71
+ if (FORBIDDEN_KEY.test(key)) {
72
+ out[key] = "[redacted]";
73
+ continue;
74
+ }
75
+ out[key] = scrubValue((value as Record<string, unknown>)[key], depth + 1);
76
+ }
77
+ return out;
78
+ }
79
+ return "[unsupported]";
80
+ }
81
+
82
+ /**
83
+ * Provider-controlled text can carry network and account identifiers that the
84
+ * Compatibility Lab privacy contract forbids persisting. The rules below run in
85
+ * a fixed total order; the order is load-bearing and documented in
86
+ * `devlog/_fin/260810_release_train_and_triage/040_sec02_remediation.md`:
87
+ *
88
+ * email before hostname - else the hostname rule eats `example.com` in an address
89
+ * MAC before IPv6 - else `01:23:45:67:89:ab` matches as a mangled address
90
+ * IPv6 before IPv4 - else `::ffff:192.0.2.128` degrades to `::ffff:[ip]`
91
+ * HTTP before other URIs - and the URI rule refuses http(s) so it cannot
92
+ * re-match this rule's own `https://[host]/path` output
93
+ *
94
+ * Every rule replaces a value whole or not at all. A prefix replacement is
95
+ * worse than no rule, because the output looks redacted while the tail leaks.
96
+ */
97
+
98
+ /** Bounded, non-nested. */
99
+ const JWT_RE = /\beyJ[A-Za-z0-9_-]{8,512}\.[A-Za-z0-9_-]{8,512}\.[A-Za-z0-9_-]{8,512}\b/g;
100
+ // Both patterns end with a lookahead rather than `\b`, because `\b` fires
101
+ // between a letter and `-`: `ops@…xn--p1ai` matched only up to the first
102
+ // hyphen and left `[email]--p1ai`, and `acct_abcdef-prod` left `[account]-prod`.
103
+ // A prefix replacement looks redacted while the tail leaks, which is the one
104
+ // thing this module must never do.
105
+ // The local part accepts more than an ASCII dot-atom: an internationalized or
106
+ // quoted local part is still the account-identifying half of the address, and
107
+ // matching only the domain left `用户@[host]` and `"ops"@[host]` behind.
108
+ const EMAIL_RE =
109
+ /(?:"[^"\n]{1,64}"|[^\s@<>()[\],;:"]{1,64})@[^\s@<>()[\],;:"]{1,255}\.(?:xn--[a-z0-9-]{1,55}|\p{L}{2,24})(?![\w.-])/giu;
110
+ // The value run includes hyphens so `acct_abcdef-prod` is consumed whole. A
111
+ // `\b`-terminated form matched only `acct_abcdef` and left `-prod` dangling
112
+ // after the token, which reads as redacted while leaking the remainder.
113
+ const PREFIXED_ACCOUNT_RE = /\b(?:acct|cus|sub|org)[_-][A-Za-z0-9][A-Za-z0-9-]{5,63}(?![\w-])/g;
114
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
115
+ // `\b` treats `_` as a word character, so it does not fire between `_` and a
116
+ // digit: `backend_203.0.113.7_timeout` and `iface_01:23:45:67:89:ab_down` are
117
+ // ordinary machine-generated diagnostics that slipped past a `\b` form. These
118
+ // use explicit non-identifier boundaries on both sides instead.
119
+ // Colon notation is usual on POSIX, hyphen notation on Windows; separators
120
+ // must be consistent within one address.
121
+ const MAC_RE = /(?<![0-9A-Za-z:-])(?:(?:[0-9A-Fa-f]{2}:){5}|(?:[0-9A-Fa-f]{2}-){5})[0-9A-Fa-f]{2}(?![0-9A-Za-z:-])/g;
122
+ const IPV4_RE = /(?<![0-9A-Za-z.])(?:\d{1,3}\.){3}\d{1,3}(?![0-9A-Za-z.])/g;
123
+ // The final label is either an ordinary alphabetic TLD or a punycode one.
124
+ // Allowing hyphens and digits in every final label was too broad: it ate
125
+ // ordinary diagnostic tokens such as `foo.bar-baz` and `metric.p95`, which
126
+ // costs exactly the evidence quality the Lab exists to capture.
127
+ // A trailing `-` or word character after the TLD means this was never a
128
+ // hostname (`foo.bar-baz`), so the lookahead refuses the match. A trailing
129
+ // `.` is allowed and consumed: it is FQDN root notation and equally ordinary
130
+ // sentence punctuation, and forbidding it let `api.example.com.` through
131
+ // untouched.
132
+ //
133
+ // The leading `(?<![\w.-])` matters just as much: without it the engine
134
+ // backtracks to a later starting label and redacts a prefix, turning
135
+ // `provider.metric.p95` into `[host].p95` — evidence destruction dressed up
136
+ // as a redaction.
137
+ // An OPTIONAL trailing dot does not work: the engine simply skips it and takes
138
+ // a shorter match, so `provider.metric.p95` matched `provider.metric` and left
139
+ // `[host].p95`. The terminator is therefore mandatory and expressed as a
140
+ // single alternation — either the token ends cleanly, or it ends with an FQDN
141
+ // root dot that is itself followed by nothing label-shaped.
142
+ const HOSTNAME_RE = /(?<![\w.-])(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,8}(?:xn--[a-z0-9-]{1,55}|[a-z]{2,24})(?:\.(?![\w-])|(?![\w.-]))/gi;
143
+ // The value is scanned to its delimiter, not to a word boundary: `\b` stops at
144
+ // the first `.` in `db_prod.internal`, which left `[host].internal` — a partial
145
+ // redaction that still names the host.
146
+ // `db.prod-1` and `api.us-east-1` are indistinguishable from ordinary dotted
147
+ // diagnostics by shape alone — a numeric or hyphenated final label is both a
148
+ // legitimate internal hostname and a legitimate metric or version namespace.
149
+ // Shape cannot decide it, so context does: these forms are redacted only when
150
+ // an unambiguous network marker introduces them. Outside such a marker they
151
+ // survive, and that is a recorded limit rather than an oversight.
152
+ //
153
+ // The marker grammar accepts the separators these messages actually use —
154
+ // whitespace, `=`, `:`, and JSON quoting — and the CANDIDATE IS VALIDATED
155
+ // before replacement. Accepting any following token turned
156
+ // `ETIMEDOUT after 30 seconds` into `ETIMEDOUT [host] 30 seconds`: a redaction
157
+ // that destroys the diagnostic and hides nothing.
158
+ // Markers differ in confidence, and treating them alike cost accuracy both
159
+ // ways. STRONG markers are resolver/socket errors and `host=`: whatever
160
+ // follows is a host by construction, so a bare `redis` or `localhost` counts.
161
+ // WEAK markers appear in ordinary prose (`upstream provider.metric.p95
162
+ // exceeded`), so they only redact a candidate that is already host-shaped.
163
+ const STRONG_HOST_CONTEXT_RE =
164
+ /\b(?:ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ETIMEDOUT|EHOSTUNREACH|dial\s+(?:tcp|udp)|connect(?:ing)?\s+to|host)["']?\s*[\s=:]\s*["']?((?:[A-Za-z0-9_.-]{1,255}[\s:]{1,3}){0,3}[A-Za-z0-9_.-]{1,255})/gi;
165
+ const WEAK_HOST_CONTEXT_RE =
166
+ /\b(?:upstream)["']?\s*[\s=:]\s*["']?([A-Za-z0-9_.-]{1,255})/gi;
167
+
168
+ /**
169
+ * A dotted run of plain alphabetic words with no digits or hyphens in any
170
+ * label — `provider.metric.p95` reads as a namespace, not a host. Under a
171
+ * WEAK marker this is left alone; under a STRONG one the marker decides.
172
+ */
173
+ const DOTTED_NAMESPACE_RE = /^[a-z]+(?:\.[a-z]+)*\.[a-z]+[0-9]*$/i;
174
+
175
+ /**
176
+ * Does this token look like a host rather than an English word?
177
+ *
178
+ * Even a strong marker does not guarantee the very next token is the
179
+ * destination: Go emits `dial tcp: lookup db.prod-1: no such host`, and errno
180
+ * codes are routinely followed by prose (`ETIMEDOUT request after 30s`).
181
+ * Replacing whatever follows destroyed the helper word and left the real host
182
+ * in place — the worst of both outcomes.
183
+ *
184
+ * A stopword list would repeat the delimiter-enumeration mistake, so the
185
+ * candidate is validated instead. A token qualifies when it carries host
186
+ * punctuation (dot, hyphen, underscore, digit) or is a reserved name; a bare
187
+ * English word does not — EXCEPT directly after a resolver marker, where the
188
+ * argument is a name by construction and `ENOTFOUND redis` must still redact.
189
+ */
190
+ const RESERVED_HOST_NAMES = new Set(["localhost", "broadcasthost"]);
191
+ const PROSE_AFTER_MARKER = new Set([
192
+ "lookup", "request", "connection", "attempt", "failed", "error", "timeout",
193
+ "after", "to", "for", "at", "no", "such", "the", "a", "an", "while", "during",
194
+ ]);
195
+ function isHostCandidate(value: string, bareWordAllowed = false): boolean {
196
+ const lower = value.toLowerCase();
197
+ if (RESERVED_HOST_NAMES.has(lower)) return true;
198
+ if (/[.\-_0-9]/.test(value)) {
199
+ return AMBIGUOUS_HOST_RE.test(value) || CONTEXTUAL_HOST_TOKEN_RE.test(value);
200
+ }
201
+ // A bare word: only a resolver marker makes it a host, and only when it is
202
+ // not one of the connective words those messages actually use.
203
+ return bareWordAllowed && !PROSE_AFTER_MARKER.has(lower);
204
+ }
205
+
206
+ /**
207
+ * A dotted label run whose final label may be numeric or hyphenated — the
208
+ * shape that is ambiguous between an internal hostname and a metric namespace.
209
+ * A single-label token is NOT a host: accepting one turned
210
+ * `upstream request failed` into `upstream [host] failed`.
211
+ */
212
+ const AMBIGUOUS_HOST_RE = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,8}[a-z0-9][a-z0-9-]{0,61}$/i;
213
+
214
+ /**
215
+ * A single-label internal name (`db-primary`, `db_prod.internal`'s owner). A
216
+ * bare word is only treated as a host inside an explicit network context, and
217
+ * it must still look like a name rather than an English word carrying the
218
+ * message — `request` and `after` are rejected by requiring a digit, hyphen,
219
+ * underscore, or dot somewhere in the token.
220
+ */
221
+ const CONTEXTUAL_HOST_TOKEN_RE = /^[a-z0-9]+(?:[._-][a-z0-9]+)+$|^[a-z]+[0-9][a-z0-9]*$/i;
222
+ // `userID` is at least as common as `userId` in provider payloads, so the id
223
+ // suffix is matched case-insensitively.
224
+ // Case-insensitive throughout: `UserID`, `USER_ID`, and `AccountId` are all
225
+ // realistic field spellings, and matching only the lowercase base label left
226
+ // them intact.
227
+ // The id suffix may be joined (`userID`), underscored (`user_id`) or spaced
228
+ // (`User ID:`) — all three are ordinary provider phrasing.
229
+ const ACCOUNT_LABEL_RE = /\b(?:user|account|organization|org|tenant|workspace|project|customer)([\s_-]?id)?\b/gi;
230
+ const IDENTIFIER_ONLY_RE = /^[A-Za-z0-9_-]+$/;
231
+ const UNQUOTED_TERMINATOR = /[\s,;)\]}]/;
232
+
233
+ function isPrefixedAccount(value: string): boolean {
234
+ PREFIXED_ACCOUNT_RE.lastIndex = 0;
235
+ const m = PREFIXED_ACCOUNT_RE.exec(value);
236
+ PREFIXED_ACCOUNT_RE.lastIndex = 0;
237
+ return m?.[0] === value;
238
+ }
239
+
240
+ /**
241
+ * A retained URL path keeps an error message diagnosable, but its segments are
242
+ * provider-controlled: `/users/<uuid>` or `/orgs/acct_…` carries an account
243
+ * identifier past the host rewrite. Replace whole segments that are an
244
+ * identifier shape; leave route names alone.
245
+ */
246
+ function scrubUrlPath(pathname: string): string {
247
+ return pathname
248
+ .split("/")
249
+ .map((segment) => {
250
+ if (!segment) return segment;
251
+ // Decode first, then re-split: an encoded slash (`%2F`) means the
252
+ // segment is itself a path, and matching only whole segments let
253
+ // `/u/%2F<uuid>` through. Decoding repeats to a fixed point because a
254
+ // single pass left `%252D` reversible.
255
+ const decoded = decodeToFixedPoint(segment);
256
+ if (isIdentifierShape(decoded)) return "[account]";
257
+ // Do NOT enumerate delimiters. Chasing them cost four rounds of review:
258
+ // first `/`, then `?#&=`, then `:` action suffixes and `;` matrix
259
+ // parameters would have been next. Instead, find identifier shapes
260
+ // wherever they appear in the decoded text; whatever separates them is
261
+ // irrelevant.
262
+ const replaced = redactIdentifiersInText(decoded);
263
+ return replaced === decoded ? segment : replaced;
264
+ })
265
+ .join("/");
266
+ }
267
+
268
+ function isIdentifierShape(value: string): boolean {
269
+ return UUID_RE.test(value) || isPrefixedAccount(value);
270
+ }
271
+
272
+ const UUID_ANYWHERE_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
273
+
274
+ /**
275
+ * Replace identifier shapes anywhere in a decoded path, independent of the
276
+ * punctuation around them. Delimiter enumeration is a losing game: colon
277
+ * action suffixes (`/ops/<uuid>:cancel`) and matrix parameters
278
+ * (`<uuid>;retry=1`) are both ordinary provider API syntax, and the next
279
+ * unlisted separator would leak again.
280
+ */
281
+ function redactIdentifiersInText(value: string): string {
282
+ return value.replace(UUID_ANYWHERE_RE, "[account]").replace(PREFIXED_ACCOUNT_RE, "[account]");
283
+ }
284
+
285
+ /** Percent-decode until the value stops changing, bounded against a decode bomb. */
286
+ function decodeToFixedPoint(value: string): string {
287
+ let current = value;
288
+ for (let pass = 0; pass < 6 && current.includes("%"); pass += 1) {
289
+ let next: string;
290
+ try {
291
+ next = decodeURIComponent(current);
292
+ } catch {
293
+ return current; // malformed escapes: treat what we have as opaque
294
+ }
295
+ if (next === current) break;
296
+ current = next;
297
+ }
298
+ return current;
299
+ }
300
+
301
+ /** Valid IPv4 dotted quad: every octet in range and canonically written. */
302
+ function isIpv4(value: string): boolean {
303
+ const parts = value.split(".");
304
+ if (parts.length !== 4) return false;
305
+ return parts.every((p) => p.length >= 1 && p.length <= 3 && /^\d+$/.test(p) && Number(p) <= 255);
306
+ }
307
+
308
+ /** Full IPv6 validation, including `::` compression and a trailing mapped IPv4. */
309
+ function isIpv6(value: string): boolean {
310
+ if (!value.includes(":")) return false;
311
+ let head = value;
312
+ let tailGroups = 0;
313
+ const lastColon = head.lastIndexOf(":");
314
+ const afterLastColon = head.slice(lastColon + 1);
315
+ if (afterLastColon.includes(".")) {
316
+ if (!isIpv4(afterLastColon)) return false;
317
+ head = head.slice(0, lastColon + 1);
318
+ tailGroups = 2;
319
+ } else if (head.includes(".")) {
320
+ return false;
321
+ }
322
+ const doubleColons = head.split("::").length - 1;
323
+ if (doubleColons > 1) return false;
324
+ const compressed = doubleColons === 1;
325
+ const [leftRaw = "", rightRaw = ""] = compressed ? head.split("::") : [head, ""];
326
+ const splitGroups = (part: string): string[] | null => {
327
+ if (part === "" || part === ":") return [];
328
+ const trimmed = part.replace(/^:|:$/g, "");
329
+ if (trimmed === "") return [];
330
+ const groups = trimmed.split(":");
331
+ return groups.every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g)) ? groups : null;
332
+ };
333
+ const left = splitGroups(leftRaw);
334
+ const right = compressed ? splitGroups(rightRaw) : [];
335
+ if (left === null || right === null) return false;
336
+ const total = left.length + right.length + tailGroups;
337
+ return compressed ? total <= 7 : total === 8;
338
+ }
339
+
340
+ /**
341
+ * Two-stage IPv6 replacement: scan the address alphabet, validate the base,
342
+ * then extend the replaced span across an optional `%zone`. A single-alphabet
343
+ * scan would leave `fe80::1%en0` as `[ip]%en0`.
344
+ */
345
+ function redactIpv6(value: string): string {
346
+ let out = "";
347
+ let i = 0;
348
+ while (i < value.length) {
349
+ const ch = value[i]!;
350
+ if (!/[0-9A-Fa-f:.]/.test(ch)) {
351
+ out += ch;
352
+ i += 1;
353
+ continue;
354
+ }
355
+ let end = i;
356
+ while (end < value.length && /[0-9A-Fa-f:.]/.test(value[end]!) && end - i < 45) end += 1;
357
+ // The candidate alphabet includes `.` for IPv4-mapped forms, so a trailing
358
+ // sentence period is swallowed too and `2001:db8::1.` failed validation as
359
+ // a whole. Retry on progressively shorter prefixes so ordinary punctuation
360
+ // after an address cannot defeat the rule.
361
+ let matched = 0;
362
+ for (let stop = end; stop > i; stop -= 1) {
363
+ // A candidate may not end on `.` — that is sentence punctuation after an
364
+ // address. It MAY end on `:`, because `2001:db8::` and `fe80::` are valid
365
+ // compressed forms; rejecting every trailing colon skipped them entirely.
366
+ // `isIpv6` is the arbiter, so a stray single trailing colon simply fails
367
+ // validation and the loop backs off one more character.
368
+ if (value[stop - 1] === ".") continue;
369
+ if (isIpv6(value.slice(i, stop))) {
370
+ matched = stop;
371
+ break;
372
+ }
373
+ }
374
+ if (matched > i) {
375
+ let zoneEnd = matched;
376
+ if (value[zoneEnd] === "%") {
377
+ let z = zoneEnd + 1;
378
+ while (z < value.length && /[A-Za-z0-9_.-]/.test(value[z]!) && z - zoneEnd <= 64) z += 1;
379
+ if (z > zoneEnd + 1) zoneEnd = z;
380
+ }
381
+ out += "[ip]";
382
+ i = zoneEnd;
383
+ continue;
384
+ }
385
+ out += value.slice(i, end);
386
+ i = end;
387
+ }
388
+ return out;
389
+ }
390
+
391
+ /** Scan a delimited span (UNC path, non-HTTP URI) and replace it whole. */
392
+ function redactScannedSpans(value: string, start: RegExp, token: string, accept?: (span: string) => boolean): string {
393
+ let out = "";
394
+ let i = 0;
395
+ while (i < value.length) {
396
+ start.lastIndex = i;
397
+ const m = start.exec(value);
398
+ if (!m) {
399
+ out += value.slice(i);
400
+ break;
401
+ }
402
+ let end = m.index + m[0].length;
403
+ while (end < value.length && !/[\s"']/.test(value[end]!)) end += 1;
404
+ const span = value.slice(m.index, end);
405
+ out += value.slice(i, m.index);
406
+ out += accept && !accept(span) ? span : token;
407
+ i = end;
408
+ }
409
+ return out;
410
+ }
411
+
412
+ /**
413
+ * Account identifiers under a label. The value span is resolved from its
414
+ * syntax (quote pair, or run to a terminator) BEFORE it is validated, so an
415
+ * out-of-grammar character or a missing closing quote leaves the text
416
+ * untouched instead of producing a partially redacted prefix.
417
+ *
418
+ * ID-bearing labels (`user_id`, `accountId`, ...) redact any value of six or
419
+ * more characters. Bare labels (`user`, `account`, ...) redact only an
420
+ * unambiguous account shape, because `org: engineering` is ordinary prose.
421
+ */
422
+ function redactContextualAccounts(value: string): string {
423
+ let out = "";
424
+ let cursor = 0;
425
+ ACCOUNT_LABEL_RE.lastIndex = 0;
426
+ let label: RegExpExecArray | null;
427
+ while ((label = ACCOUNT_LABEL_RE.exec(value)) !== null) {
428
+ if (label.index < cursor) continue;
429
+ const idBearing = Boolean(label[1]);
430
+ let p = label.index + label[0].length;
431
+ // A JSON field name is quoted: `"userId": "v"`. Step over the label's own
432
+ // closing quote before looking for the separator.
433
+ if (value[p] === '"' || value[p] === "'") p += 1;
434
+ while (p < value.length && /\s/.test(value[p]!)) p += 1;
435
+ if (value[p] !== "=" && value[p] !== ":") continue;
436
+ p += 1;
437
+ while (p < value.length && /\s/.test(value[p]!)) p += 1;
438
+ const quote = value[p] === '"' || value[p] === "'" ? value[p]! : "";
439
+ let spanStart = quote ? p + 1 : p;
440
+ let spanEnd: number;
441
+ if (quote) {
442
+ const close = value.indexOf(quote, spanStart);
443
+ if (close === -1) continue;
444
+ spanEnd = close;
445
+ } else {
446
+ spanEnd = spanStart;
447
+ while (spanEnd < value.length && !UNQUOTED_TERMINATOR.test(value[spanEnd]!)) spanEnd += 1;
448
+ }
449
+ const span = value.slice(spanStart, spanEnd);
450
+ if (!span || !IDENTIFIER_ONLY_RE.test(span)) continue;
451
+ const redact = idBearing ? span.length >= 6 : isPrefixedAccount(span) || UUID_RE.test(span);
452
+ if (!redact) continue;
453
+ out += value.slice(cursor, spanStart) + "[account]";
454
+ cursor = spanEnd;
455
+ ACCOUNT_LABEL_RE.lastIndex = spanEnd;
456
+ }
457
+ out += value.slice(cursor);
458
+ return out;
459
+ }
460
+
461
+ function scrubString(value: string): string {
462
+ // 1. Existing secret redaction.
463
+ let s = redactSecretString(value);
464
+ s = s.replace(SECRETISH_GLOBAL, "[REDACTED]");
465
+ // 2. Filesystem paths, including UNC shares.
466
+ s = s.replace(/(?:[A-Za-z]:\\|\/(?:home|Users|tmp|var|etc|root|mnt)\/)[^\s"']+/g, "[path]");
467
+ s = redactScannedSpans(s, /\\\\[^\\\s"']{1,255}\\/g, "[path]");
468
+ // 3. HTTP(S) URLs. The retained pathname keeps the diagnostic useful, so its
469
+ // segments are scrubbed of identifier shapes rather than trusted: a
470
+ // provider error citing `/users/<uuid>` would otherwise persist an
471
+ // account id that the host rewrite alone does not touch.
472
+ s = s.replace(/https?:\/\/[^\s"']+/gi, (url) => {
473
+ try {
474
+ const u = new URL(url);
475
+ if (u.username || u.password) return "[redacted-url]";
476
+ const path = scrubUrlPath(u.pathname);
477
+ if (/^(localhost|127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/i.test(u.hostname)) {
478
+ return `${u.protocol}//[private-host]${path}`;
479
+ }
480
+ return `${u.protocol}//[host]${path}`;
481
+ } catch {
482
+ return "[redacted-url]";
483
+ }
484
+ });
485
+ // 4. Other-scheme URIs, which may carry credentials. Refuses http(s) so it
486
+ // cannot re-match the output of rule 3.
487
+ s = redactScannedSpans(s, /\b[a-z][a-z0-9+.-]{1,15}:\/\//gi, "[uri]", (span) => !/^https?:\/\//i.test(span));
488
+ // 5-8. Bounded token shapes.
489
+ s = s.replace(JWT_RE, "[jwt]");
490
+ s = s.replace(EMAIL_RE, "[email]");
491
+ s = s.replace(PREFIXED_ACCOUNT_RE, "[account]");
492
+ s = redactContextualAccounts(s);
493
+ s = s.replace(MAC_RE, "[mac]");
494
+ // 9-10. Addresses: IPv6 first so mapped forms are replaced whole.
495
+ s = redactIpv6(s);
496
+ s = s.replace(IPV4_RE, (m) => (isIpv4(m) ? "[ip]" : m));
497
+ // 11. Multi-label hostnames, plus single-label names in a host-bearing context.
498
+ s = s.replace(HOSTNAME_RE, "[host]");
499
+ s = s.replace(STRONG_HOST_CONTEXT_RE, (m, tail: string) => {
500
+ // Scan the few tokens after the marker for the first host-shaped one:
501
+ // Go writes `dial tcp: lookup <host>: no such host`, so the destination
502
+ // is not always adjacent to the marker. A resolver marker also licenses a
503
+ // bare name (`ENOTFOUND redis`), which a socket-state marker does not.
504
+ // A name paired with a port is a destination whatever else is true:
505
+ // `dial tcp redis:6379` needs no other evidence. Both notations count —
506
+ // adjacent `host:443` and spelled-out `gateway on port 443` — because the
507
+ // port is the evidence, not the punctuation.
508
+ const ported =
509
+ tail.match(/(?<![\w.-])([A-Za-z0-9_.-]{1,255}):\d{1,5}(?![\w.])/) ??
510
+ tail.match(/(?<![\w.-])([A-Za-z0-9_.-]{1,255})\s+(?:on\s+)?port\s+\d{1,5}\b/i);
511
+ if (ported?.[1] && !PROSE_AFTER_MARKER.has(ported[1].toLowerCase())) {
512
+ return m.replace(ported[1], "[host]");
513
+ }
514
+ // Otherwise only a resolver marker licenses a bare name. Natural-language
515
+ // `connect to` does not: `Unable to connect to your account` is prose.
516
+ const resolver = /ENOTFOUND|EAI_AGAIN|lookup|host/i.test(m);
517
+ for (const token of tail.split(/[\s:]+/)) {
518
+ if (token && isHostCandidate(token, resolver)) return m.replace(token, "[host]");
519
+ }
520
+ return m;
521
+ });
522
+ s = s.replace(WEAK_HOST_CONTEXT_RE, (m, name: string) =>
523
+ CONTEXTUAL_HOST_TOKEN_RE.test(name) && !DOTTED_NAMESPACE_RE.test(name) ? m.replace(name, "[host]") : m,
524
+ );
525
+ const bytes = new TextEncoder().encode(s);
526
+ if (bytes.byteLength > MAX_SANITIZED_STRING_FIELD) {
527
+ return new TextDecoder().decode(bytes.slice(0, MAX_SANITIZED_STRING_FIELD));
528
+ }
529
+ return s;
530
+ }
531
+
532
+ const TRUNCATION_MARKERS = [
533
+ "[REDACTED]",
534
+ "[redacted-url]",
535
+ "[private-host]",
536
+ "[account]",
537
+ "[email]",
538
+ "[host]",
539
+ "[path]",
540
+ "[uri]",
541
+ "[jwt]",
542
+ "[mac]",
543
+ "[ip]",
544
+ ];
545
+
546
+ /**
547
+ * Byte-bounded truncation for persisted summaries.
548
+ *
549
+ * `String.prototype.slice` counts UTF-16 code units, so it can split a
550
+ * surrogate pair or leave a partial `[em` fragment that a reader could mistake
551
+ * for literal content. Bytes are the right unit because
552
+ * `enforceEventStructureLimits` also measures encoded byte length.
553
+ */
554
+ export function truncateUtf8(value: string, maxBytes: number): string {
555
+ const encoder = new TextEncoder();
556
+ if (encoder.encode(value).byteLength <= maxBytes) return value;
557
+ let cut = 0;
558
+ let used = 0;
559
+ for (const char of value) {
560
+ const size = encoder.encode(char).byteLength;
561
+ if (used + size > maxBytes) break;
562
+ used += size;
563
+ cut += char.length;
564
+ }
565
+ // If the cut lands strictly inside a marker, drop the whole marker: a
566
+ // fragment such as `[accoun` reads as literal content. Sanitization has
567
+ // already replaced the raw value, so the marker's opening bracket is a safe
568
+ // cut point and everything before it is intact.
569
+ const open = value.lastIndexOf("[", cut - 1);
570
+ if (open !== -1) {
571
+ const tail = value.slice(open, cut);
572
+ for (const marker of TRUNCATION_MARKERS) {
573
+ if (tail.length < marker.length && marker.startsWith(tail)) return value.slice(0, open);
574
+ }
575
+ }
576
+ return value.slice(0, cut);
577
+ }
578
+
579
+ /** Stable privacy boundary for diagnostic text that may be persisted. */
580
+ export function sanitizeDiagnostic(value: unknown): string {
581
+ return scrubString(value instanceof Error ? value.message : String(value));
582
+ }
583
+
584
+ export function sanitizedJsonBytes(value: unknown): Uint8Array {
585
+ return new TextEncoder().encode(jcsStringify(scrubValue(value, 0)));
586
+ }