machine-bridge-mcp 2.0.0 → 3.0.0-beta.102

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 (473) hide show
  1. package/CHANGELOG.md +1142 -0
  2. package/CONTRIBUTING.md +23 -25
  3. package/GOVERNANCE.md +2 -2
  4. package/README.md +58 -20
  5. package/SECURITY.md +161 -120
  6. package/SUPPORT.md +1 -1
  7. package/browser-extension/broker-auth.js +83 -0
  8. package/browser-extension/broker-liveness.js +35 -0
  9. package/browser-extension/browser-error-boundary.js +96 -0
  10. package/browser-extension/browser-operations.js +1456 -101
  11. package/browser-extension/devtools-input.js +195 -34
  12. package/browser-extension/devtools-observation.js +429 -0
  13. package/browser-extension/devtools-session.js +48 -0
  14. package/browser-extension/manifest.json +4 -3
  15. package/browser-extension/page-automation.js +578 -118
  16. package/browser-extension/pairing-bootstrap.js +42 -0
  17. package/browser-extension/pairing.js +28 -12
  18. package/browser-extension/service-worker.js +126 -106
  19. package/docs/AGENT_CONTEXT.md +13 -10
  20. package/docs/ARCHITECTURE.md +159 -67
  21. package/docs/AUDIT.md +1302 -4
  22. package/docs/CLIENTS.md +19 -6
  23. package/docs/COMPUTER_USE.md +297 -0
  24. package/docs/ENGINEERING.md +72 -13
  25. package/docs/GETTING_STARTED.md +18 -20
  26. package/docs/LOCAL_AUTHORIZATION.md +115 -72
  27. package/docs/LOCAL_AUTOMATION.md +27 -21
  28. package/docs/LOGGING.md +40 -19
  29. package/docs/MANAGED_JOBS.md +37 -34
  30. package/docs/MULTI_ACCOUNT.md +98 -37
  31. package/docs/OPERATIONS.md +145 -51
  32. package/docs/OVERVIEW.md +14 -11
  33. package/docs/POLICY_REFERENCE.md +71 -3
  34. package/docs/PRIVACY.md +41 -2
  35. package/docs/PROJECT_STANDARDS.md +29 -6
  36. package/docs/RELEASING.md +238 -64
  37. package/docs/TESTING.md +150 -39
  38. package/docs/THREAT_MODEL.md +164 -67
  39. package/docs/TOOL_REFERENCE.md +653 -60
  40. package/docs/UPGRADING.md +78 -18
  41. package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
  42. package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
  43. package/native/macos/MachineBridgeTrustBroker.swift +233 -0
  44. package/package.json +94 -16
  45. package/scripts/accepted-candidate-tarball.mjs +77 -0
  46. package/scripts/candidate-runtime-store.mjs +93 -0
  47. package/scripts/check-plan.mjs +73 -3
  48. package/scripts/check-runner.mjs +129 -17
  49. package/scripts/commit-message-check.mjs +5 -2
  50. package/scripts/consumer-package-security.mjs +293 -0
  51. package/scripts/coverage-check.mjs +251 -15
  52. package/scripts/coverage-generation.mjs +41 -0
  53. package/scripts/coverage-range-merge.mjs +41 -0
  54. package/scripts/foreground-daemon-recovery.mjs +88 -0
  55. package/scripts/generate-policy-reference.mjs +28 -5
  56. package/scripts/generate-tool-reference.mjs +1 -1
  57. package/scripts/generate-worker-types.mjs +21 -11
  58. package/scripts/github-backlog.mjs +11 -10
  59. package/scripts/github-push.mjs +62 -16
  60. package/scripts/github-release-asset.mjs +58 -0
  61. package/scripts/github-release.mjs +241 -132
  62. package/scripts/global-package-installation.mjs +68 -0
  63. package/scripts/hardened-npm-session.mjs +42 -0
  64. package/scripts/install-published-prerelease.mjs +198 -0
  65. package/scripts/local-release-acceptance.mjs +103 -45
  66. package/scripts/macos-background-input-smoke.mjs +515 -0
  67. package/scripts/network-retry.mjs +8 -1
  68. package/scripts/npm-global-prefix.mjs +27 -0
  69. package/scripts/npm-publication-policy.mjs +17 -0
  70. package/scripts/official-mcp-conformance.mjs +267 -0
  71. package/scripts/persistent-activation-process.mjs +101 -0
  72. package/scripts/prepare-pinned-npm.mjs +12 -69
  73. package/scripts/prerelease-activation.mjs +160 -0
  74. package/scripts/privacy-check.mjs +14 -6
  75. package/scripts/promotion-digest.mjs +128 -0
  76. package/scripts/publish-npm.mjs +260 -0
  77. package/scripts/published-release.mjs +132 -0
  78. package/scripts/release-acceptance.mjs +62 -54
  79. package/scripts/release-browser-extension-store.mjs +172 -0
  80. package/scripts/release-candidate-manifest.mjs +52 -0
  81. package/scripts/release-channel.mjs +106 -0
  82. package/scripts/release-diagnostic.mjs +41 -0
  83. package/scripts/release-impact-check.mjs +12 -22
  84. package/scripts/release-oauth-canary-core.mjs +325 -0
  85. package/scripts/release-oauth-canary-evidence.mjs +100 -0
  86. package/scripts/release-oauth-canary.mjs +127 -0
  87. package/scripts/release-publication-guard.mjs +67 -0
  88. package/scripts/release-soak.mjs +284 -0
  89. package/scripts/release-state.mjs +1 -1
  90. package/scripts/run-checks.mjs +44 -7
  91. package/scripts/run-worker-dry-run.mjs +21 -0
  92. package/scripts/sarif-security-gate.mjs +2 -1
  93. package/scripts/sbom-check.mjs +106 -0
  94. package/scripts/start-release-candidate.mjs +267 -44
  95. package/scripts/syntax-check.mjs +36 -14
  96. package/scripts/verification-generation-guard.mjs +15 -0
  97. package/scripts/verification-idle-sleep-guard.mjs +37 -0
  98. package/scripts/verification-state.mjs +108 -0
  99. package/scripts/wrangler-command-lifecycle.mjs +115 -0
  100. package/src/local/account-access.mjs +13 -1
  101. package/src/local/account-admin-response.mjs +92 -0
  102. package/src/local/account-admin.mjs +52 -19
  103. package/src/local/agent-context-projection.mjs +26 -7
  104. package/src/local/agent-context.mjs +88 -49
  105. package/src/local/agent-skill-discovery.mjs +16 -5
  106. package/src/local/app-automation-macos-jxa.mjs +350 -0
  107. package/src/local/app-automation.mjs +873 -163
  108. package/src/local/application-capability-projection.mjs +24 -0
  109. package/src/local/authority-context.mjs +106 -0
  110. package/src/local/autostart-log-maintenance.mjs +36 -0
  111. package/src/local/browser-bridge-http.mjs +5 -3
  112. package/src/local/browser-bridge.mjs +109 -31
  113. package/src/local/browser-broker-auth-http.mjs +67 -0
  114. package/src/local/browser-broker-auth.mjs +98 -0
  115. package/src/local/browser-broker-routes.mjs +63 -20
  116. package/src/local/browser-broker-server.mjs +17 -7
  117. package/src/local/browser-command.mjs +79 -30
  118. package/src/local/browser-computer-observation-service.mjs +266 -0
  119. package/src/local/browser-extension-identity.mjs +50 -0
  120. package/src/local/browser-extension-path.mjs +22 -0
  121. package/src/local/browser-extension-protocol.mjs +50 -11
  122. package/src/local/browser-operation-service.mjs +147 -117
  123. package/src/local/browser-pairing-grant.mjs +109 -0
  124. package/src/local/browser-pairing-http.mjs +36 -0
  125. package/src/local/browser-pairing-launch.mjs +58 -0
  126. package/src/local/browser-pairing-store.mjs +85 -48
  127. package/src/local/browser-request-registry.mjs +40 -14
  128. package/src/local/browser-request-settlement.mjs +78 -0
  129. package/src/local/browser-resource-input.mjs +74 -0
  130. package/src/local/browser-trusted-input-health.mjs +63 -0
  131. package/src/local/call-authority.mjs +21 -0
  132. package/src/local/call-capacity.mjs +45 -0
  133. package/src/local/call-registry-drain.mjs +54 -0
  134. package/src/local/call-registry.mjs +30 -15
  135. package/src/local/capability-observer.mjs +5 -0
  136. package/src/local/capability-ranking.mjs +3 -2
  137. package/src/local/child-process-settlement.mjs +132 -0
  138. package/src/local/cli-account-admin.mjs +40 -6
  139. package/src/local/cli-activate.mjs +162 -0
  140. package/src/local/cli-local-admin.mjs +20 -45
  141. package/src/local/cli-options.mjs +12 -13
  142. package/src/local/cli-ready-output.mjs +52 -0
  143. package/src/local/cli-service.mjs +91 -11
  144. package/src/local/cli.mjs +232 -150
  145. package/src/local/computer-use-application-observation.mjs +224 -0
  146. package/src/local/computer-use-arguments.mjs +270 -0
  147. package/src/local/computer-use-deadline.mjs +50 -0
  148. package/src/local/computer-use-dispatch-settlement.mjs +40 -0
  149. package/src/local/computer-use-expectation.mjs +171 -0
  150. package/src/local/computer-use-observation.mjs +769 -0
  151. package/src/local/computer-use-recovery.mjs +82 -0
  152. package/src/local/computer-use-result-budget.mjs +145 -0
  153. package/src/local/computer-use-snapshot-store.mjs +67 -0
  154. package/src/local/computer-use.mjs +2255 -0
  155. package/src/local/daemon-http-relay-auth.mjs +26 -0
  156. package/src/local/daemon-http-relay-connection.mjs +298 -0
  157. package/src/local/daemon-http-relay-request.mjs +64 -0
  158. package/src/local/daemon-http-relay-sequence.mjs +49 -0
  159. package/src/local/daemon-process.mjs +40 -7
  160. package/src/local/delegated-process-sandbox.mjs +148 -0
  161. package/src/local/device-identity.mjs +156 -49
  162. package/src/local/device-root-provider.mjs +81 -0
  163. package/src/local/directory-metadata.mjs +47 -0
  164. package/src/local/doctor-reporting.mjs +23 -0
  165. package/src/local/durable-process-spec.mjs +80 -0
  166. package/src/local/errors.mjs +25 -15
  167. package/src/local/exclusive-file.mjs +74 -26
  168. package/src/local/execution-limits.mjs +9 -0
  169. package/src/local/execution-routing.mjs +276 -0
  170. package/src/local/execution-surface.mjs +23 -0
  171. package/src/local/file-mutation-coordinator.mjs +47 -0
  172. package/src/local/file-snapshot-preservation.mjs +24 -0
  173. package/src/local/filesystem-identity.mjs +54 -0
  174. package/src/local/fixed-process-environment.mjs +21 -0
  175. package/src/local/full-access-test.mjs +31 -13
  176. package/src/local/git-commit.mjs +69 -0
  177. package/src/local/git-config-safety.mjs +55 -0
  178. package/src/local/git-log-parser.mjs +41 -0
  179. package/src/local/git-metadata-boundary.mjs +77 -0
  180. package/src/local/git-metadata-tree-safety.mjs +59 -0
  181. package/src/local/git-operation-state.mjs +19 -0
  182. package/src/local/git-service.mjs +165 -30
  183. package/src/local/hardened-npm-download-timeout.mjs +46 -0
  184. package/src/local/hardened-npm-download.mjs +109 -0
  185. package/src/local/hardened-npm-extract.mjs +20 -0
  186. package/src/local/hardened-npm-verification.mjs +104 -0
  187. package/src/local/hardened-npm.mjs +199 -0
  188. package/src/local/job-runner.mjs +244 -89
  189. package/src/local/lifecycle.mjs +6 -0
  190. package/src/local/log.mjs +19 -45
  191. package/src/local/macos-background-input.mjs +398 -0
  192. package/src/local/macos-idle-sleep-assertion.mjs +65 -0
  193. package/src/local/macos-trust-broker.mjs +404 -0
  194. package/src/local/managed-job-cancellation.mjs +42 -0
  195. package/src/local/managed-job-capacity.mjs +16 -0
  196. package/src/local/managed-job-directory-generation.mjs +60 -0
  197. package/src/local/managed-job-directory.mjs +75 -0
  198. package/src/local/managed-job-durable-process.mjs +49 -0
  199. package/src/local/managed-job-lock.mjs +46 -14
  200. package/src/local/managed-job-plan.mjs +38 -14
  201. package/src/local/managed-job-projection.mjs +23 -15
  202. package/src/local/managed-job-retention.mjs +128 -0
  203. package/src/local/managed-job-runner-claim.mjs +70 -0
  204. package/src/local/managed-job-runner.mjs +35 -10
  205. package/src/local/managed-job-storage.mjs +22 -6
  206. package/src/local/managed-job-terminal-maintenance.mjs +40 -0
  207. package/src/local/managed-job-terminal.mjs +150 -0
  208. package/src/local/managed-jobs.mjs +424 -287
  209. package/src/local/npm-cli.mjs +42 -0
  210. package/src/local/npm-environment.mjs +37 -0
  211. package/src/local/operation-authorization.mjs +68 -324
  212. package/src/local/operation-risk.mjs +51 -4
  213. package/src/local/owner-state-lock.mjs +118 -0
  214. package/src/local/package-identity.mjs +9 -0
  215. package/src/local/patch.mjs +20 -18
  216. package/src/local/path-inspection.mjs +23 -0
  217. package/src/local/policy.mjs +19 -0
  218. package/src/local/private-toolchain-integrity.mjs +54 -0
  219. package/src/local/process-execution.mjs +87 -75
  220. package/src/local/process-foreground-timeout.mjs +50 -0
  221. package/src/local/process-identity.mjs +41 -7
  222. package/src/local/process-nonreplayable-settlement.mjs +50 -0
  223. package/src/local/process-result-projection.mjs +22 -0
  224. package/src/local/process-session-events.mjs +47 -0
  225. package/src/local/process-session-remote-activity.mjs +10 -0
  226. package/src/local/process-session-termination.mjs +73 -0
  227. package/src/local/process-sessions.mjs +144 -112
  228. package/src/local/process-tracker.mjs +140 -13
  229. package/src/local/process-tree-ownership-types.d.ts +37 -0
  230. package/src/local/process-tree-ownership.mjs +57 -0
  231. package/src/local/process-tree-signal.mjs +34 -0
  232. package/src/local/process-tree-snapshot.mjs +77 -0
  233. package/src/local/process-tree-supervisor.mjs +69 -0
  234. package/src/local/process-tree.mjs +11 -53
  235. package/src/local/project-metadata.mjs +19 -6
  236. package/src/local/relay-call-recovery.mjs +58 -38
  237. package/src/local/relay-connection-classification.mjs +131 -0
  238. package/src/local/relay-connection-support.mjs +91 -0
  239. package/src/local/relay-connection.mjs +179 -272
  240. package/src/local/relay-diagnostics.mjs +91 -0
  241. package/src/local/relay-heartbeat.mjs +119 -0
  242. package/src/local/relay-liveness.mjs +109 -0
  243. package/src/local/relay-peer-diagnostics.mjs +29 -0
  244. package/src/local/release-runtime-lock.mjs +18 -0
  245. package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
  246. package/src/local/remote-configuration.mjs +48 -0
  247. package/src/local/resilient-relay-connection.mjs +178 -0
  248. package/src/local/resource-admission-diagnostics.mjs +52 -0
  249. package/src/local/resource-admission-policy.mjs +115 -0
  250. package/src/local/resource-admission.mjs +339 -0
  251. package/src/local/resource-build-root.mjs +83 -0
  252. package/src/local/resource-cargo-concurrency.mjs +38 -0
  253. package/src/local/resource-cmake-concurrency.mjs +42 -0
  254. package/src/local/resource-command-concurrency.mjs +37 -0
  255. package/src/local/resource-command-profile.mjs +176 -0
  256. package/src/local/resource-coordinator-accounting.mjs +21 -0
  257. package/src/local/resource-cpu-window.mjs +20 -0
  258. package/src/local/resource-disk-headroom.mjs +13 -0
  259. package/src/local/resource-elastic-memory.mjs +19 -0
  260. package/src/local/resource-elastic-request.mjs +36 -0
  261. package/src/local/resource-foreground-wait.mjs +29 -0
  262. package/src/local/resource-go-concurrency.mjs +43 -0
  263. package/src/local/resource-gradle-concurrency.mjs +57 -0
  264. package/src/local/resource-host-cache.mjs +39 -0
  265. package/src/local/resource-host-darwin.mjs +54 -0
  266. package/src/local/resource-host-linux.mjs +35 -0
  267. package/src/local/resource-host-sample-file.mjs +23 -0
  268. package/src/local/resource-host-snapshot.mjs +99 -0
  269. package/src/local/resource-lease-accounting.mjs +119 -0
  270. package/src/local/resource-light-command.mjs +6 -0
  271. package/src/local/resource-make-concurrency.mjs +42 -0
  272. package/src/local/resource-maven-concurrency.mjs +38 -0
  273. package/src/local/resource-ninja-command-concurrency.mjs +20 -0
  274. package/src/local/resource-ninja-concurrency.mjs +28 -0
  275. package/src/local/resource-operations.mjs +26 -10
  276. package/src/local/resource-pressure.mjs +67 -0
  277. package/src/local/resource-probe-command.mjs +35 -0
  278. package/src/local/resource-process-admission.mjs +59 -0
  279. package/src/local/resource-process-ancestry-cache.mjs +43 -0
  280. package/src/local/resource-process-ancestry.mjs +32 -0
  281. package/src/local/resource-process-priority.mjs +15 -0
  282. package/src/local/resource-project-key.mjs +53 -0
  283. package/src/local/resource-pytest-concurrency.mjs +33 -0
  284. package/src/local/resource-release-control-classification.mjs +14 -0
  285. package/src/local/resource-release-control-executable.mjs +70 -0
  286. package/src/local/resource-release-control-workspace.mjs +41 -0
  287. package/src/local/resource-request-contract.mjs +30 -0
  288. package/src/local/resource-script-classification.mjs +44 -0
  289. package/src/local/resource-shell-analysis.mjs +58 -0
  290. package/src/local/resource-staging-recovery.mjs +97 -0
  291. package/src/local/resource-swift-concurrency.mjs +31 -0
  292. package/src/local/resource-transaction-lock.mjs +189 -0
  293. package/src/local/resource-wait.mjs +48 -0
  294. package/src/local/resource-waiters.mjs +139 -0
  295. package/src/local/resource-xcode-command.mjs +7 -0
  296. package/src/local/resource-xcode-concurrency.mjs +34 -0
  297. package/src/local/resource-xcode-non-build.mjs +19 -0
  298. package/src/local/runtime-activation.mjs +700 -0
  299. package/src/local/runtime-activity-projection.mjs +48 -0
  300. package/src/local/runtime-capabilities.mjs +48 -15
  301. package/src/local/runtime-diagnostic-state.mjs +14 -0
  302. package/src/local/runtime-diagnostics.mjs +40 -13
  303. package/src/local/runtime-info-projection.mjs +60 -0
  304. package/src/local/runtime-info-relay-projection.mjs +21 -0
  305. package/src/local/runtime-paths.mjs +2 -1
  306. package/src/local/runtime-process-routing.mjs +33 -0
  307. package/src/local/runtime-relay-connection-options.mjs +44 -0
  308. package/src/local/runtime-relay-control.mjs +98 -0
  309. package/src/local/runtime-relay.mjs +26 -50
  310. package/src/local/runtime-reporting.mjs +37 -21
  311. package/src/local/runtime-resource-service.mjs +77 -0
  312. package/src/local/runtime-tool-handlers.mjs +13 -10
  313. package/src/local/runtime.mjs +244 -208
  314. package/src/local/secure-file.mjs +73 -20
  315. package/src/local/security-audit-dispatch.mjs +35 -0
  316. package/src/local/security-audit-log.mjs +215 -0
  317. package/src/local/security-audit-state.mjs +139 -0
  318. package/src/local/security-audit-storage.mjs +135 -0
  319. package/src/local/security-audit-warning.mjs +31 -0
  320. package/src/local/security-audit-worker.mjs +87 -0
  321. package/src/local/service-convergence.mjs +42 -1
  322. package/src/local/service-definition.mjs +24 -0
  323. package/src/local/service-environment.mjs +18 -10
  324. package/src/local/service-owner.mjs +146 -0
  325. package/src/local/service-ownership.mjs +17 -0
  326. package/src/local/service-restart-handoff.mjs +64 -0
  327. package/src/local/service-restart-scheduler.mjs +49 -0
  328. package/src/local/service-runtime-convergence.mjs +52 -0
  329. package/src/local/service-runtime.mjs +105 -0
  330. package/src/local/service-status.mjs +60 -0
  331. package/src/local/service.mjs +385 -92
  332. package/src/local/shell.mjs +39 -19
  333. package/src/local/ssh-key.mjs +158 -42
  334. package/src/local/state-inventory.mjs +50 -24
  335. package/src/local/state-owner-lock-inventory.mjs +23 -0
  336. package/src/local/state-root-owned-namespaces.mjs +83 -0
  337. package/src/local/state-root-retirement.mjs +89 -0
  338. package/src/local/state.mjs +386 -96
  339. package/src/local/stdio.mjs +96 -66
  340. package/src/local/support-state-projection.mjs +30 -0
  341. package/src/local/system-network-route.mjs +76 -0
  342. package/src/local/systemd-removal.mjs +20 -0
  343. package/src/local/tool-executor.mjs +85 -20
  344. package/src/local/tool-result-boundary.mjs +51 -0
  345. package/src/local/toolchain-operation-lock.mjs +41 -0
  346. package/src/local/tools.mjs +7 -5
  347. package/src/local/trusted-executable.mjs +53 -0
  348. package/src/local/trusted-git-executable.mjs +34 -0
  349. package/src/local/trusted-github-cli.mjs +24 -0
  350. package/src/local/windows-service-convergence.mjs +49 -0
  351. package/src/local/windows-service.mjs +52 -45
  352. package/src/local/worker-deployment-fingerprint.mjs +102 -0
  353. package/src/local/worker-deployment.mjs +48 -60
  354. package/src/local/worker-health.mjs +12 -4
  355. package/src/local/worker-secret-file.mjs +37 -17
  356. package/src/local/workspace-file-service.mjs +156 -239
  357. package/src/local/workspace-file-transaction.mjs +189 -0
  358. package/src/local/workspace-search.mjs +55 -0
  359. package/src/local/wrangler-toolchain/package-lock.json +1505 -0
  360. package/src/local/wrangler-toolchain/package.json +23 -0
  361. package/src/local/wrangler-toolchain-verification.mjs +135 -0
  362. package/src/local/wrangler-toolchain.mjs +188 -0
  363. package/src/shared/access-contract.json +7 -1
  364. package/src/shared/activation-recovery.mjs +49 -0
  365. package/src/shared/admin-auth.d.mts +2 -1
  366. package/src/shared/admin-auth.mjs +3 -3
  367. package/src/shared/authority-revocation.d.mts +9 -0
  368. package/src/shared/authority-revocation.mjs +28 -0
  369. package/src/shared/daemon-auth.d.mts +11 -0
  370. package/src/shared/daemon-auth.mjs +17 -0
  371. package/src/shared/device-session-auth.d.mts +20 -0
  372. package/src/shared/device-session-auth.mjs +51 -0
  373. package/src/shared/foreground-timeout.d.mts +15 -0
  374. package/src/shared/foreground-timeout.mjs +70 -0
  375. package/src/shared/log-redaction.d.mts +9 -0
  376. package/src/shared/log-redaction.mjs +51 -0
  377. package/src/shared/mcp-protocol.d.mts +27 -0
  378. package/src/shared/mcp-protocol.mjs +275 -0
  379. package/src/shared/project-overview-projection.d.mts +4 -0
  380. package/src/shared/project-overview-projection.mjs +96 -0
  381. package/src/shared/relay-contract.json +28 -0
  382. package/src/shared/result-projection.d.mts +2 -1
  383. package/src/shared/result-projection.mjs +13 -2
  384. package/src/shared/server-metadata.json +15 -9
  385. package/src/shared/tool-argument-validation.d.mts +17 -0
  386. package/src/shared/tool-argument-validation.mjs +325 -0
  387. package/src/shared/tool-call-capacity.d.mts +41 -0
  388. package/src/shared/tool-call-capacity.mjs +59 -0
  389. package/src/shared/tool-catalog.json +626 -57
  390. package/src/worker/access.ts +12 -0
  391. package/src/worker/account-admin.ts +55 -33
  392. package/src/worker/authority-revocations.ts +174 -0
  393. package/src/worker/authority.ts +52 -6
  394. package/src/worker/daemon-auth.ts +49 -68
  395. package/src/worker/daemon-channel.ts +36 -0
  396. package/src/worker/daemon-http-auth.ts +63 -0
  397. package/src/worker/daemon-http-channel.ts +120 -0
  398. package/src/worker/daemon-http-controller.ts +163 -0
  399. package/src/worker/daemon-http-protocol.ts +62 -0
  400. package/src/worker/daemon-http-registry.ts +82 -0
  401. package/src/worker/daemon-last-observation.ts +30 -0
  402. package/src/worker/daemon-liveness.ts +8 -6
  403. package/src/worker/daemon-ready-dispatch.ts +17 -0
  404. package/src/worker/daemon-ready-messages.ts +73 -0
  405. package/src/worker/daemon-ready-waiters.ts +79 -0
  406. package/src/worker/daemon-recovery-budget.ts +13 -0
  407. package/src/worker/daemon-registry.ts +58 -0
  408. package/src/worker/daemon-relay-diagnostics.ts +127 -0
  409. package/src/worker/daemon-socket-attachment.ts +55 -0
  410. package/src/worker/daemon-sockets.ts +72 -62
  411. package/src/worker/daemon-status.ts +38 -0
  412. package/src/worker/device-session-verifier.ts +129 -0
  413. package/src/worker/dpop.ts +157 -0
  414. package/src/worker/durable-process-timeout.ts +29 -0
  415. package/src/worker/errors.ts +57 -0
  416. package/src/worker/http.ts +112 -26
  417. package/src/worker/index.ts +446 -440
  418. package/src/worker/mcp-access.ts +57 -0
  419. package/src/worker/mcp-controller.ts +188 -0
  420. package/src/worker/mcp-http-accept.ts +11 -0
  421. package/src/worker/mcp-http-contract.ts +322 -0
  422. package/src/worker/mcp-initialization-compat.ts +141 -0
  423. package/src/worker/mcp-jsonrpc.ts +11 -29
  424. package/src/worker/mcp-removed-protocol.ts +17 -0
  425. package/src/worker/mcp-response-proxy.ts +135 -0
  426. package/src/worker/mcp-response-stream.ts +65 -0
  427. package/src/worker/mcp-stale-schema-compat.ts +57 -0
  428. package/src/worker/mcp-stream-proxy-contract.ts +48 -0
  429. package/src/worker/mcp-tool-call-input.ts +23 -0
  430. package/src/worker/nonce-store.ts +33 -35
  431. package/src/worker/oauth-authorization-page.ts +21 -1
  432. package/src/worker/oauth-client-admin.ts +52 -0
  433. package/src/worker/oauth-client-contract.ts +38 -0
  434. package/src/worker/oauth-controller.ts +107 -47
  435. package/src/worker/oauth-field-contract.ts +31 -0
  436. package/src/worker/oauth-record-contract.ts +7 -0
  437. package/src/worker/oauth-refresh-authority.ts +27 -0
  438. package/src/worker/oauth-refresh-exchange.ts +162 -0
  439. package/src/worker/oauth-refresh-families.ts +130 -27
  440. package/src/worker/oauth-refresh-persistence.ts +116 -0
  441. package/src/worker/oauth-state.ts +33 -18
  442. package/src/worker/oauth-store-validation.ts +131 -0
  443. package/src/worker/oauth-token-derivation.ts +33 -0
  444. package/src/worker/oauth-token-issuance.ts +119 -0
  445. package/src/worker/oauth-tokens.ts +56 -190
  446. package/src/worker/observability.ts +38 -5
  447. package/src/worker/pending-admission.ts +15 -0
  448. package/src/worker/pending-call-capacity.ts +108 -0
  449. package/src/worker/pending-call-contract.ts +42 -5
  450. package/src/worker/pending-call-deadlines.ts +75 -0
  451. package/src/worker/pending-calls.ts +131 -125
  452. package/src/worker/runtime-alarm-storage.ts +37 -0
  453. package/src/worker/runtime-alarm.ts +136 -0
  454. package/src/worker/server-info-activity.ts +36 -0
  455. package/src/worker/server-info-tool-delivery.ts +15 -0
  456. package/src/worker/server-info.ts +168 -0
  457. package/src/worker/tool-catalog.ts +47 -1
  458. package/src/worker/tool-timeout.ts +75 -14
  459. package/src/worker/websocket-protocol.ts +26 -2
  460. package/src/worker/worker-edge-guard.ts +90 -0
  461. package/src/worker/worker-edge-log.ts +65 -0
  462. package/src/worker/worker-entry.ts +52 -0
  463. package/src/worker/worker-mcp-config.ts +23 -0
  464. package/src/worker/worker-metadata.ts +48 -0
  465. package/src/worker/worker-rate-limit-key.ts +30 -0
  466. package/src/worker/worker-runtime-config.ts +20 -0
  467. package/src/worker/worker-static-routes.ts +54 -0
  468. package/src/worker/worker-task-lifetime.ts +18 -0
  469. package/tsconfig.local.json +17 -1
  470. package/wrangler.jsonc +13 -0
  471. package/src/local/cli-approval.mjs +0 -117
  472. package/src/local/operation-state-lock.mjs +0 -92
  473. package/src/worker/mcp-session.ts +0 -72
@@ -0,0 +1,118 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import path from "node:path";
3
+ import { createExclusiveFileSync, removeOwnedJsonFileSync } from "./exclusive-file.mjs";
4
+ import { createMonotonicDeadline } from "./monotonic-deadline.mjs";
5
+ import { currentProcessStartTimeMs, inspectProcessInstance } from "./process-identity.mjs";
6
+ import { ensureOwnerOnlyDirectorySync, readBoundedRegularFileSync, retryTransientMultipleLinksSync } from "./secure-file.mjs";
7
+
8
+ const MAX_LOCK_BYTES = 8 * 1024;
9
+ const DEFAULT_WAIT_MS = 5_000;
10
+ const DEFAULT_POLL_MS = 25;
11
+
12
+ export async function withOwnerStateLock(root, callback, options = {}) {
13
+ if (typeof callback !== "function") throw new TypeError("owner-state lock requires a callback");
14
+ const requestedRoot = String(root || "").trim();
15
+ if (!requestedRoot) throw new Error("owner-state lock root is missing");
16
+ const purpose = boundedIdentifier(options.purpose, "state");
17
+ const fileName = boundedFileName(options.fileName, `${purpose}.lock`);
18
+ const label = String(options.label || purpose).replace(/[\r\n\t\u0000-\u001f\u007f]/g, " ").trim().slice(0, 80) || purpose;
19
+ const directory = path.resolve(requestedRoot);
20
+ ensureOwnerOnlyDirectorySync(directory);
21
+ const lockPath = path.join(directory, fileName);
22
+ const timeoutMs = positiveInteger(options.timeoutMs, DEFAULT_WAIT_MS);
23
+ const pollMs = positiveInteger(options.pollMs, DEFAULT_POLL_MS);
24
+ const maxAgeMs = Number.isFinite(Number(options.maxAgeMs)) ? Math.max(1, Number(options.maxAgeMs)) : Number.POSITIVE_INFINITY;
25
+ const deadline = createMonotonicDeadline(timeoutMs);
26
+ const owner = lockOwner(purpose);
27
+
28
+ while (true) {
29
+ try {
30
+ createExclusiveFileSync(lockPath, `${JSON.stringify(owner, null, 2)}\n`, { mode: 0o600 });
31
+ break;
32
+ } catch (error) {
33
+ if (error?.code !== "EEXIST") throw error;
34
+ const inspected = retryTransientMultipleLinksSync(() => readOwnerStateLock(lockPath, purpose));
35
+ if (inspected.kind === "missing") continue;
36
+ if (inspected.kind === "invalid") {
37
+ throw new Error(`${label} lock is malformed; inspect the owner-only state directory`);
38
+ }
39
+ const existing = inspected.owner;
40
+ const identity = inspectProcessInstance(existing, { maxAgeMs });
41
+ if (identity.reclaimable) {
42
+ if (removeOwnedJsonFileSync(lockPath, { token: existing.token, purpose }, { maxBytes: MAX_LOCK_BYTES })) continue;
43
+ }
44
+ if (deadline.expired()) {
45
+ const pid = Number.isInteger(existing.pid) ? `pid ${existing.pid}` : "unknown process";
46
+ throw new Error(`${label} state is busy (${pid}); retry after the current operation finishes`);
47
+ }
48
+ await delay(Math.min(pollMs, Math.max(1, deadline.remainingMs())));
49
+ }
50
+ }
51
+
52
+ let result;
53
+ let callbackError;
54
+ try {
55
+ result = await callback();
56
+ } catch (error) {
57
+ callbackError = error;
58
+ }
59
+ let releaseError;
60
+ try {
61
+ const released = removeOwnedJsonFileSync(lockPath, { token: owner.token, purpose }, { maxBytes: MAX_LOCK_BYTES });
62
+ if (!released) releaseError = new Error(`${label} lock changed before release; state may require inspection`);
63
+ } catch (error) {
64
+ releaseError = new Error(`${label} lock release failed; state may require inspection`, { cause: error });
65
+ }
66
+ if (callbackError && releaseError) {
67
+ throw new AggregateError([callbackError, releaseError], `${label} operation failed and lock release was incomplete`);
68
+ }
69
+ if (releaseError) throw releaseError;
70
+ if (callbackError) throw callbackError;
71
+ return result;
72
+ }
73
+
74
+ function lockOwner(purpose) {
75
+ return {
76
+ pid: process.pid,
77
+ token: randomBytes(16).toString("hex"),
78
+ purpose,
79
+ startedAt: new Date().toISOString(),
80
+ processStartedAt: new Date(currentProcessStartTimeMs()).toISOString(),
81
+ entryScript: String(process.argv[1] || "").slice(0, 4096),
82
+ };
83
+ }
84
+
85
+ export function readOwnerStateLock(file, purpose) {
86
+ let text;
87
+ try { text = readBoundedRegularFileSync(file, MAX_LOCK_BYTES, "owner-state lock", { verifyPathIdentity: true, rejectMultipleLinks: true }).toString("utf8"); }
88
+ catch (error) { if (error?.code === "ENOENT") return { kind: "missing" }; throw error; }
89
+ let parsed;
90
+ try { parsed = JSON.parse(text); } catch { return { kind: "invalid" }; }
91
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { kind: "invalid" };
92
+ if (!Number.isInteger(parsed.pid) || parsed.pid <= 0) return { kind: "invalid" };
93
+ if (!/^[a-f0-9]{32}$/.test(String(parsed.token || ""))) return { kind: "invalid" };
94
+ if (parsed.purpose !== purpose) return { kind: "invalid" };
95
+ if (!Number.isFinite(Date.parse(String(parsed.startedAt || "")))) return { kind: "invalid" };
96
+ if (!Number.isFinite(Date.parse(String(parsed.processStartedAt || "")))) return { kind: "invalid" };
97
+ return { kind: "owner", owner: parsed };
98
+ }
99
+
100
+ function boundedIdentifier(value, fallback) {
101
+ const text = String(value || fallback).toLowerCase().replace(/[^a-z0-9._-]/g, "-").slice(0, 64);
102
+ return text || fallback;
103
+ }
104
+
105
+ function boundedFileName(value, fallback) {
106
+ const name = path.basename(String(value || fallback));
107
+ if (!/^[a-z0-9][a-z0-9._-]{0,95}\.lock$/.test(name)) throw new Error("owner-state lock filename is invalid");
108
+ return name;
109
+ }
110
+
111
+ function positiveInteger(value, fallback) {
112
+ const number = Number(value);
113
+ return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;
114
+ }
115
+
116
+ function delay(milliseconds) {
117
+ return new Promise((resolvePromise) => { setTimeout(resolvePromise, milliseconds); });
118
+ }
@@ -0,0 +1,9 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import serverMetadata from "../shared/server-metadata.json" with { type: "json" };
4
+ import packageManifest from "../../package.json" with { type: "json" };
5
+
6
+ export const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
7
+ export const packageName = String(packageManifest.name);
8
+ export const packageVersion = String(packageManifest.version);
9
+ export const appName = String(serverMetadata.name);
@@ -1,3 +1,5 @@
1
+ import { BridgeError } from "./errors.mjs";
2
+
1
3
  const BEGIN = "*** Begin Patch";
2
4
  const END = "*** End Patch";
3
5
  const SECTION_PREFIXES = ["*** Add File: ", "*** Update File: ", "*** Delete File: "];
@@ -5,10 +7,10 @@ const SECTION_PREFIXES = ["*** Add File: ", "*** Update File: ", "*** Delete Fil
5
7
  export function parsePatchEnvelope(input) {
6
8
  const text = String(input ?? "").replaceAll("\r\n", "\n");
7
9
  const lines = text.split("\n");
8
- if (lines[0] !== BEGIN) throw new Error(`patch must start with ${BEGIN}`);
10
+ if (lines[0] !== BEGIN) throw new BridgeError("invalid_request", `patch must start with ${BEGIN}`, { details: { reason: "missing_begin_marker" } });
9
11
  const endIndex = lines.lastIndexOf(END);
10
- if (endIndex < 1) throw new Error(`patch must end with ${END}`);
11
- if (lines.slice(endIndex + 1).some((line) => line !== "")) throw new Error("unexpected content after patch envelope");
12
+ if (endIndex < 1) throw new BridgeError("invalid_request", `patch must end with ${END}`, { details: { reason: "missing_end_marker" } });
13
+ if (lines.slice(endIndex + 1).some((line) => line !== "")) throw new BridgeError("invalid_request", "unexpected content after patch envelope", { details: { reason: "trailing_content" } });
12
14
 
13
15
  const operations = [];
14
16
  let index = 1;
@@ -25,17 +27,17 @@ export function parsePatchEnvelope(input) {
25
27
  : header.startsWith(SECTION_PREFIXES[2])
26
28
  ? "delete"
27
29
  : "";
28
- if (!kind) throw new Error(`invalid patch section header at line ${index + 1}`);
30
+ if (!kind) throw new BridgeError("invalid_request", `invalid patch section header at line ${index + 1}`, { details: { reason: "invalid_section_header", line: index + 1 } });
29
31
  const prefix = SECTION_PREFIXES[kind === "add" ? 0 : kind === "update" ? 1 : 2];
30
32
  const path = header.slice(prefix.length).trim();
31
- if (!path) throw new Error(`patch ${kind} path is empty`);
33
+ if (!path) throw new BridgeError("invalid_request", `patch ${kind} path is empty`, { details: { reason: "empty_path", operation: kind } });
32
34
  index += 1;
33
35
 
34
36
  if (kind === "add") {
35
37
  const contentLines = [];
36
38
  while (index < endIndex && !isSectionHeader(lines[index])) {
37
39
  const line = lines[index];
38
- if (!line.startsWith("+")) throw new Error(`added file content must start with + at line ${index + 1}`);
40
+ if (!line.startsWith("+")) throw new BridgeError("invalid_request", `added file content must start with + at line ${index + 1}`, { details: { reason: "invalid_add_line", line: index + 1 } });
39
41
  contentLines.push(line.slice(1));
40
42
  index += 1;
41
43
  }
@@ -45,7 +47,7 @@ export function parsePatchEnvelope(input) {
45
47
 
46
48
  if (kind === "delete") {
47
49
  if (index < endIndex && !isSectionHeader(lines[index]) && lines[index] !== "") {
48
- throw new Error(`delete section must not contain patch body at line ${index + 1}`);
50
+ throw new BridgeError("invalid_request", `delete section must not contain patch body at line ${index + 1}`, { details: { reason: "unexpected_delete_body", line: index + 1 } });
49
51
  }
50
52
  operations.push({ kind, path });
51
53
  continue;
@@ -54,7 +56,7 @@ export function parsePatchEnvelope(input) {
54
56
  let moveTo = "";
55
57
  if (lines[index]?.startsWith("*** Move to: ")) {
56
58
  moveTo = lines[index].slice("*** Move to: ".length).trim();
57
- if (!moveTo) throw new Error("move destination is empty");
59
+ if (!moveTo) throw new BridgeError("invalid_request", "move destination is empty", { details: { reason: "empty_move_destination" } });
58
60
  index += 1;
59
61
  }
60
62
  const hunks = [];
@@ -63,38 +65,38 @@ export function parsePatchEnvelope(input) {
63
65
  index += 1;
64
66
  continue;
65
67
  }
66
- if (!lines[index].startsWith("@@")) throw new Error(`update hunk must start with @@ at line ${index + 1}`);
68
+ if (!lines[index].startsWith("@@")) throw new BridgeError("invalid_request", `update hunk must start with @@ at line ${index + 1}`, { details: { reason: "missing_hunk_header", line: index + 1 } });
67
69
  const headerText = lines[index].slice(2).trim();
68
70
  index += 1;
69
71
  const hunkLines = [];
70
72
  while (index < endIndex && !isSectionHeader(lines[index]) && !lines[index].startsWith("@@")) {
71
73
  const line = lines[index];
72
74
  if (!line || ![" ", "+", "-"].includes(line[0])) {
73
- throw new Error(`invalid update line prefix at line ${index + 1}`);
75
+ throw new BridgeError("invalid_request", `invalid update line prefix at line ${index + 1}`, { details: { reason: "invalid_update_line", line: index + 1 } });
74
76
  }
75
77
  hunkLines.push({ type: line[0], text: line.slice(1) });
76
78
  index += 1;
77
79
  }
78
- if (!hunkLines.length) throw new Error("empty update hunk");
79
- if (!hunkLines.some((line) => line.type !== "+")) throw new Error("update hunk needs context or removed lines");
80
+ if (!hunkLines.length) throw new BridgeError("invalid_request", "empty update hunk", { details: { reason: "empty_hunk" } });
81
+ if (!hunkLines.some((line) => line.type !== "+")) throw new BridgeError("invalid_request", "update hunk needs context or removed lines", { details: { reason: "context_free_hunk" } });
80
82
  hunks.push({ header: headerText, lines: hunkLines });
81
83
  }
82
- if (!hunks.length) throw new Error(`update section for ${path} contains no hunks`);
84
+ if (!hunks.length) throw new BridgeError("invalid_request", "update section contains no hunks", { details: { reason: "missing_hunks" } });
83
85
  operations.push({ kind, path, moveTo: moveTo || undefined, hunks });
84
86
  }
85
87
 
86
- if (!operations.length) throw new Error("patch contains no file operations");
88
+ if (!operations.length) throw new BridgeError("invalid_request", "patch contains no file operations", { details: { reason: "empty_patch" } });
87
89
  const touched = new Set();
88
90
  for (const operation of operations) {
89
91
  for (const path of [operation.path, operation.moveTo].filter(Boolean)) {
90
- if (touched.has(path)) throw new Error(`patch touches the same path more than once: ${path}`);
92
+ if (touched.has(path)) throw new BridgeError("conflict", "patch touches the same path more than once", { details: { reason: "duplicate_path" } });
91
93
  touched.add(path);
92
94
  }
93
95
  }
94
96
  return operations;
95
97
  }
96
98
 
97
- export function applyUpdateHunks(content, hunks, path = "file") {
99
+ export function applyUpdateHunks(content, hunks) {
98
100
  const normalized = String(content).replaceAll("\r\n", "\n");
99
101
  const trailingNewline = normalized.endsWith("\n");
100
102
  let lines = normalized.split("\n");
@@ -106,10 +108,10 @@ export function applyUpdateHunks(content, hunks, path = "file") {
106
108
  const newLines = hunk.lines.filter((line) => line.type !== "-").map((line) => line.text);
107
109
  const matches = findSequenceMatches(lines, oldLines, searchFrom);
108
110
  if (matches.length === 0) {
109
- throw new Error(`patch context not found for ${path} hunk ${hunkIndex + 1}`);
111
+ throw new BridgeError("conflict", "patch context did not match current file state", { details: { reason: "context_not_found", hunk: hunkIndex + 1 } });
110
112
  }
111
113
  if (matches.length > 1) {
112
- throw new Error(`patch context is ambiguous for ${path} hunk ${hunkIndex + 1}`);
114
+ throw new BridgeError("conflict", "patch context matched more than once", { details: { reason: "context_ambiguous", hunk: hunkIndex + 1 } });
113
115
  }
114
116
  const at = matches[0];
115
117
  lines.splice(at, oldLines.length, ...newLines);
@@ -0,0 +1,23 @@
1
+ import { lstat, opendir } from "node:fs/promises";
2
+
3
+ export function isMissingPathError(error) {
4
+ return error?.code === "ENOENT";
5
+ }
6
+
7
+ export async function pathEntryIfExists(path, inspect = lstat) {
8
+ try {
9
+ return await inspect(path);
10
+ } catch (error) {
11
+ if (isMissingPathError(error)) return null;
12
+ throw error;
13
+ }
14
+ }
15
+
16
+ export async function openDirectoryIfExists(path, openDirectory = opendir) {
17
+ try {
18
+ return await openDirectory(path);
19
+ } catch (error) {
20
+ if (isMissingPathError(error)) return null;
21
+ throw error;
22
+ }
23
+ }
@@ -111,6 +111,25 @@ export function normalizePolicy(policy = {}) {
111
111
  return Object.freeze(normalized);
112
112
  }
113
113
 
114
+ /** @param {PolicyInput} left @param {PolicyInput} right @param {{profile?: string, origin?: string}} [options] */
115
+ export function intersectPolicies(left, right, options = {}) {
116
+ const a = normalizePolicy(left);
117
+ const b = normalizePolicy(right);
118
+ const rank = { off: 0, direct: 1, shell: 2 };
119
+ const execMode = rank[a.execMode] <= rank[b.execMode] ? a.execMode : b.execMode;
120
+ return Object.freeze({
121
+ profile: typeof options.profile === "string" && options.profile ? options.profile : "custom",
122
+ origin: typeof options.origin === "string" && options.origin ? options.origin : "custom",
123
+ revision: Math.max(a.revision, b.revision),
124
+ allowWrite: a.allowWrite && b.allowWrite,
125
+ allowExec: execMode !== "off",
126
+ execMode,
127
+ unrestrictedPaths: a.unrestrictedPaths && b.unrestrictedPaths,
128
+ minimalEnv: a.minimalEnv || b.minimalEnv,
129
+ exposeAbsolutePaths: a.exposeAbsolutePaths && b.exposeAbsolutePaths,
130
+ });
131
+ }
132
+
114
133
  /** @param {PolicyCapabilities} left @param {PolicyCapabilities} right */
115
134
  export function policyCapabilitiesEqual(left, right) {
116
135
  return left.allowWrite === right.allowWrite
@@ -0,0 +1,54 @@
1
+ const OPERATIONAL_ERROR_CODES = new Set([
2
+ "EACCES",
3
+ "EPERM",
4
+ "EIO",
5
+ "ENOSPC",
6
+ "EDQUOT",
7
+ "ENOMEM",
8
+ "EROFS",
9
+ "EMFILE",
10
+ "ENFILE",
11
+ "EAGAIN",
12
+ "ENOBUFS",
13
+ "EINTR",
14
+ "ESTALE",
15
+ "EBUSY",
16
+ "ETIMEDOUT",
17
+ ]);
18
+
19
+ export class PrivateToolchainIntegrityError extends Error {
20
+ constructor(message, options = {}) {
21
+ super(message, options);
22
+ this.name = "PrivateToolchainIntegrityError";
23
+ this.code = "private_toolchain_integrity_invalid";
24
+ }
25
+ }
26
+
27
+ export function privateToolchainIntegrityError(message, cause = undefined) {
28
+ return new PrivateToolchainIntegrityError(message, cause === undefined ? {} : { cause });
29
+ }
30
+
31
+ export function isPrivateToolchainIntegrityError(error) {
32
+ return error instanceof PrivateToolchainIntegrityError
33
+ || error?.code === "private_toolchain_integrity_invalid";
34
+ }
35
+
36
+ export function throwOperationalOrIntegrity(error, message) {
37
+ if (error instanceof TypeError || error instanceof RangeError || operationalErrorCode(error)) throw error;
38
+ throw privateToolchainIntegrityError(message, error);
39
+ }
40
+
41
+ export function operationalErrorCode(error) {
42
+ const pending = [error];
43
+ const seen = new Set();
44
+ while (pending.length) {
45
+ const current = pending.shift();
46
+ if (!current || typeof current !== "object" || seen.has(current)) continue;
47
+ seen.add(current);
48
+ const code = String(current.code || "");
49
+ if (OPERATIONAL_ERROR_CODES.has(code)) return code;
50
+ if (current.cause) pending.push(current.cause);
51
+ if (Array.isArray(current.errors)) pending.push(...current.errors);
52
+ }
53
+ return "";
54
+ }