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,52 @@
1
+ const LOGGABLE_ADMISSION_REASONS = new Set([
2
+ "resource_capacity", "project_resource_busy", "host_pressure_red", "disk_reserve_floor",
3
+ "cpu_reservation", "io_reservation", "memory_reservation", "cpu_pressure_window",
4
+ "cpu_request_exceeds_launch_window", "fairness_wait", "coordinator_busy", "resource_busy",
5
+ ]);
6
+ const LOGGABLE_PRESSURE_STATES = new Set(["green", "yellow", "red", "unknown"]);
7
+
8
+ export function resourceAdmissionGuardrailSnapshot() {
9
+ return {
10
+ scope: "machine-user-cross-process",
11
+ model: "work-conserving-multi-resource",
12
+ durable_process_group_leases: true,
13
+ priority_aging: true,
14
+ light_operations_bypass: true,
15
+ default_compiler_jobs: 3,
16
+ hard_resource_quota: false,
17
+ };
18
+ }
19
+
20
+ export function resourceAdmissionLogFields(error) {
21
+ if (error?.details?.reason !== "resource_admission") return {};
22
+ const reason = String(error.details.admission_reason || "resource_busy");
23
+ const pressure = String(error.details.pressure_state || "unknown");
24
+ return {
25
+ resource_admission_reason: LOGGABLE_ADMISSION_REASONS.has(reason) ? reason : "resource_busy",
26
+ resource_pressure_state: LOGGABLE_PRESSURE_STATES.has(pressure) ? pressure : "unknown",
27
+ };
28
+ }
29
+
30
+ export async function resourceAdmissionDiagnostic(snapshotFunction, classifyError) {
31
+ if (typeof snapshotFunction !== "function") return { snapshot: null, check: null };
32
+ try {
33
+ const snapshot = await snapshotFunction();
34
+ return {
35
+ snapshot,
36
+ check: {
37
+ layer: "local-resource-admission",
38
+ ok: snapshot?.healthy === true,
39
+ pressure_state: snapshot?.pressure?.state || "unknown",
40
+ active_leases: Number(snapshot?.active_leases) || 0,
41
+ active_waiters: Number(snapshot?.waiters?.active) || 0,
42
+ fairness_drain_active: snapshot?.waiters?.drain_active === true,
43
+ },
44
+ };
45
+ } catch (error) {
46
+ const errorClass = classifyError(error);
47
+ return {
48
+ snapshot: { healthy: false, error_class: errorClass },
49
+ check: { layer: "local-resource-admission", ok: false, error_class: errorClass },
50
+ };
51
+ }
52
+ }
@@ -0,0 +1,115 @@
1
+ import { aggregateResourceLeases, resourceRequestIncrement } from "./resource-lease-accounting.mjs";
2
+ import { unobservedResourceCpu } from "./resource-cpu-window.mjs";
3
+ import { resourceDiskHardFloorBytes } from "./resource-disk-headroom.mjs";
4
+ import { optionalResourceNumber, resourcePressureState } from "./resource-pressure.mjs";
5
+ const NON_RETRYABLE_ADMISSION_REASONS = new Set(["cpu_request_exceeds_launch_window"]);
6
+
7
+ export function resourceAdmissionDecisionRetryable(decision) {
8
+ return !NON_RETRYABLE_ADMISSION_REASONS.has(decision?.reason);
9
+ }
10
+
11
+ export function evaluateResourceAdmission(host, leases, request, _now = Date.now(), context = {}) {
12
+ if (!request?.heavy) return { admitted: true, state: "light", reason: "light_bypass" };
13
+ const accounting = context?.accounting || {};
14
+ const cpuCores = Math.max(1, Number(host?.cpu_cores) || 1);
15
+ const totalMemoryMb = Math.max(1024, Number(host?.total_memory_mb) || 1024);
16
+ const used = aggregateResourceLeases(leases, accounting);
17
+ const unobservedCpu = unobservedResourceCpu(leases, accounting, host?.sampled_at_ms);
18
+ const pressure = resourcePressureState(host, used);
19
+ const diskReclaim = isDiskReclaimRequest(request);
20
+ const diskOnlyCritical = diskReclaim && pressure.state === "red"
21
+ && pressure.critical_reasons?.length === 1 && pressure.critical_reasons[0] === "disk_free_headroom_critical";
22
+ const limits = resourceLimits(cpuCores, totalMemoryMb, request.priority, diskOnlyCritical ? "yellow" : pressure.state, pressure.reasons);
23
+ const declared = {
24
+ cpu: request.unbounded ? limits.cpu : Math.max(0, Number(request.cpu) || 0),
25
+ io: Math.max(0, Number(request.io) || 0),
26
+ memory_mb: Math.max(0, Number(request.memory_mb) || 0),
27
+ disk_reserve_bytes: Math.max(0, Number(request.disk_reserve_bytes) || 0),
28
+ };
29
+ const requested = resourceRequestIncrement(leases, { ...request, ...declared, unbounded: false }, accounting);
30
+ const decision = {
31
+ admitted: false, state: pressure.state, reason: "resource_capacity",
32
+ pressure_reasons: pressure.reasons, limits, used, requested, reservation: declared,
33
+ cpu_window: { observed_busy_cores: optionalResourceNumber(host?.cpu_busy_cores), unobserved_reserved_cpu: unobservedCpu },
34
+ };
35
+ const bestCaseCpuLimit = resourceLimits(cpuCores, totalMemoryMb, request.priority, "green").cpu;
36
+ if (!request.unbounded && requested.cpu > bestCaseCpuLimit + 0.25) {
37
+ return { ...decision, reason: "cpu_request_exceeds_launch_window" };
38
+ }
39
+ const ignoredContention = new Set(accounting.ancestorLeaseIds || []);
40
+ if (request.contention_key && leases.some((lease) => lease?.request?.contention_key === request.contention_key
41
+ && !ignoredContention.has(lease?.lease_id))) return { ...decision, reason: "project_resource_busy" };
42
+ if (pressure.state === "red" && !diskOnlyCritical) return { ...decision, reason: "host_pressure_red" };
43
+ if (!diskReclaim && !diskReservationFits(host, used.disk_reserve_bytes, requested.disk_reserve_bytes)) return { ...decision, reason: "disk_reserve_floor" };
44
+ if (used.cpu + requested.cpu > limits.cpu * limits.cpu_overcommit + 0.01) return { ...decision, reason: "cpu_reservation" };
45
+ if (used.io + requested.io > limits.io * limits.io_overcommit + 0.01) return { ...decision, reason: "io_reservation" };
46
+ if (used.memory_mb + requested.memory_mb > limits.memory_mb * limits.memory_overcommit + 1) return { ...decision, reason: "memory_reservation" };
47
+ const currentCpu = Number.isFinite(Number(host?.cpu_busy_cores)) ? Number(host.cpu_busy_cores) : 0;
48
+ if (currentCpu + unobservedCpu + requested.cpu > limits.cpu + 0.25) return { ...decision, reason: "cpu_pressure_window" };
49
+ return { ...decision, admitted: true, reason: diskOnlyCritical ? "admitted_disk_reclaim" : pressure.state === "yellow" ? "admitted_yellow" : "admitted_green" };
50
+ }
51
+
52
+ export function resourcePressureSnapshot(host, leases, priority = "ordinary", _now = Date.now(), accounting = {}, request = null) {
53
+ const cpuCores = Math.max(1, Number(host?.cpu_cores) || 1);
54
+ const totalMemoryMb = Math.max(1024, Number(host?.total_memory_mb) || 1024);
55
+ const used = aggregateResourceLeases(leases, accounting);
56
+ const pressure = resourcePressureState(host, used);
57
+ const observedCpu = optionalResourceNumber(host?.cpu_busy_cores);
58
+ const unobservedCpu = unobservedResourceCpu(leases, accounting, host?.sampled_at_ms);
59
+ const snapshot = { state: pressure.state, reasons: pressure.reasons, used,
60
+ observed: {
61
+ cpu_busy_cores: observedCpu,
62
+ reserved_cpu: used.cpu,
63
+ unobserved_reserved_cpu: unobservedCpu,
64
+ reserved_to_observed_cpu_ratio: observedCpu !== null && observedCpu > 0 ? Math.min(1, used.cpu / observedCpu) : null,
65
+ },
66
+ limits: resourceLimits(cpuCores, totalMemoryMb, priority, pressure.state, pressure.reasons) };
67
+ if (request?.heavy) snapshot.requested = resourceRequestIncrement(leases, request, accounting);
68
+ return snapshot;
69
+ }
70
+
71
+ export function deriveHostRates(current, previous) {
72
+ const result = { ...current };
73
+ const elapsed = (Number(current?.sampled_at_ms) - Number(previous?.sampled_at_ms)) / 1000;
74
+ if (!(elapsed > 0 && elapsed <= 30)) return result;
75
+ for (const [totalKey, rateKey] of [["pageouts_total", "pageouts_per_s"], ["swapouts_total", "swapouts_per_s"]]) {
76
+ const currentValue = Number(current?.[totalKey]); const priorValue = Number(previous?.[totalKey]);
77
+ if (Number.isFinite(currentValue) && Number.isFinite(priorValue) && currentValue >= priorValue) result[rateKey] = (currentValue - priorValue) / elapsed;
78
+ }
79
+ const totalDelta = Number(current?.cpu_time_ms_total) - Number(previous?.cpu_time_ms_total);
80
+ const idleDelta = Number(current?.cpu_idle_ms_total) - Number(previous?.cpu_idle_ms_total);
81
+ const cores = Math.max(1, Number(current?.cpu_cores) || 1);
82
+ if (Number.isFinite(totalDelta) && Number.isFinite(idleDelta) && totalDelta > 0 && idleDelta >= 0 && idleDelta <= totalDelta) {
83
+ result.cpu_busy_cores = Math.max(0, Math.min(cores, cores * (1 - idleDelta / totalDelta)));
84
+ }
85
+ return result;
86
+ }
87
+
88
+ function resourceLimits(cores, totalMemoryMb, priority, state, reasons = []) {
89
+ const headroom = priority === "background" ? 2 : priority === "interactive" ? 1 : 1.5;
90
+ const constrained = state !== "green"; const reasonSet = new Set(reasons);
91
+ const cpuPressure = constrained && (reasonSet.has("cpu_busy") || reasonSet.has("psi_cpu_some"));
92
+ const ioPressure = constrained && [...reasonSet].some((reason) => reason.startsWith("disk_") || reason.startsWith("psi_io_"));
93
+ const memoryPressure = constrained && [...reasonSet].some((reason) =>
94
+ reason.startsWith("memory_pressure") || reason.startsWith("pageout_rate") || reason.startsWith("swapout_rate") || reason.startsWith("psi_memory_"));
95
+ return {
96
+ cpu: Math.max(1, cores - headroom - (cpuPressure ? 1 : 0)),
97
+ io: ioPressure ? 1 : 1.25,
98
+ memory_mb: Math.max(1024, totalMemoryMb * (memoryPressure ? 0.55 : 0.70)),
99
+ cpu_overcommit: cpuPressure ? 1.15 : 1.35,
100
+ io_overcommit: ioPressure ? 1.05 : 1.25,
101
+ memory_overcommit: memoryPressure ? 1.10 : 1.25,
102
+ };
103
+ }
104
+ function diskReservationFits(host, usedBytes, requestedBytes) {
105
+ const free = Number(host?.disk_free_bytes); const total = Number(host?.disk_total_bytes);
106
+ if (!Number.isFinite(free) || !Number.isFinite(total) || free <= 0 || total <= 0) return true;
107
+ const hardFloor = resourceDiskHardFloorBytes(total);
108
+ return free - usedBytes - requestedBytes >= hardFloor;
109
+ }
110
+ function isDiskReclaimRequest(request) {
111
+ return request.family === "disk-reclaim" && request.resource_class === "io"
112
+ && request.unbounded === false && request.serialize_project === false
113
+ && Number(request.cpu) <= 0.25 && Number(request.io) <= 0.75
114
+ && Number(request.memory_mb) <= 256 && Number(request.disk_reserve_bytes) === 0;
115
+ }
@@ -0,0 +1,339 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { homedir } from "node:os";
3
+ import { readdirSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+ import { performance } from "node:perf_hooks";
6
+ import { createExclusiveFileSync, removeOwnedJsonFileSync, replaceFileAtomicallySync } from "./exclusive-file.mjs";
7
+ import { resourceChangeSignal, resourceRetryDelayMs, resourceSleep, signalResourceChange, waitForResourceChange } from "./resource-wait.mjs";
8
+ import { withResourceTransactionLock } from "./resource-transaction-lock.mjs";
9
+ import { ensureOwnerOnlyDirectorySync, readBoundedRegularFileSync } from "./secure-file.mjs";
10
+ import { currentProcessStartTimeMs, inspectProcessInstance, processStartTimeMsAsync } from "./process-identity.mjs";
11
+ import { freshResourceHostSnapshot, resourceHostNeedsFreshIo } from "./resource-host-cache.mjs";
12
+ import { readResourceHostSample } from "./resource-host-sample-file.mjs";
13
+ import { sampleResourceHostAsync } from "./resource-host-snapshot.mjs";
14
+ import { validateResourceRequest } from "./resource-request-contract.mjs";
15
+ import { recoverResourceDirectoryStaging, RESOURCE_STAGING_BUSY_CODE } from "./resource-staging-recovery.mjs";
16
+ import { deriveHostRates, evaluateResourceAdmission, resourceAdmissionDecisionRetryable, resourcePressureSnapshot } from "./resource-admission-policy.mjs";
17
+ import { fitElasticRequestToPressure } from "./resource-elastic-request.mjs";
18
+ import { resourceCoordinatorAccounting, resourceCoordinatorEvaluator } from "./resource-coordinator-accounting.mjs";
19
+ import { cachedResourceProcessParentSamplerAsync } from "./resource-process-ancestry-cache.mjs";
20
+ import { sampleResourceProcessParentsAsync } from "./resource-process-ancestry.mjs";
21
+ import { resourceProjectHash, resourceRequestForProject } from "./resource-project-key.mjs";
22
+ import { createResourceWaiter, pruneAndReadResourceWaiters, removeResourceWaiter, resourceWaiterDrainActive, resourceWaiterQueueSnapshot, selectedResourceWaiter } from "./resource-waiters.mjs";
23
+ const SCHEMA = 1; const PROVISIONAL_TTL_MS = 30_000; const PROCESS_OWNERSHIP_LOCK_WAIT_MS = 30_000; const LEASE_FILE = /^lease_[a-f0-9]{32}\.json$/;
24
+ export class ResourceAdmissionError extends Error {
25
+ constructor(decision) {
26
+ super(`local heavy-resource admission deferred (${decision?.reason || "resource_busy"})`);
27
+ this.name = "ResourceAdmissionError";
28
+ this.code = "MBM_RESOURCE_BUSY";
29
+ this.retryable = resourceAdmissionDecisionRetryable(decision);
30
+ this.decision = decision;
31
+ }
32
+ }
33
+ export function defaultResourceCoordinatorRoot(env = process.env) {
34
+ if (env.AGENT_RESOURCE_COORDINATOR_ROOT) return resolve(String(env.AGENT_RESOURCE_COORDINATOR_ROOT));
35
+ if (process.platform === "win32") {
36
+ const base = env.LOCALAPPDATA || env.APPDATA || homedir();
37
+ return resolve(base, "AgentResourceCoordinator");
38
+ }
39
+ const base = env.XDG_STATE_HOME ? resolve(String(env.XDG_STATE_HOME)) : join(homedir(), ".local", "state");
40
+ return join(base, "agent-resource-coordinator");
41
+ }
42
+ export class ResourceCoordinator {
43
+ constructor(options = {}) {
44
+ this.root = resolve(options.root || defaultResourceCoordinatorRoot(options.env));
45
+ this.sampleHost = options.sampleHost || sampleResourceHostAsync;
46
+ this.evaluate = options.evaluate || evaluateResourceAdmission;
47
+ this.now = options.now || Date.now;
48
+ this.sampleProcessParents = cachedResourceProcessParentSamplerAsync(options.sampleProcessParents || sampleResourceProcessParentsAsync, this.now, options.processParentCacheMs);
49
+ this.sleep = options.sleep || resourceSleep;
50
+ this.random = options.random || Math.random;
51
+ this.leasesDir = join(this.root, "leases");
52
+ this.waitersDir = join(this.root, "waiters");
53
+ this.hostSampleFile = join(this.root, "host-sample.json");
54
+ this.markerFile = join(this.root, "protocol.json");
55
+ this.hostSamplesInFlight = new Map();
56
+ this.ready = false;
57
+ }
58
+
59
+ async acquire(request, options = {}) {
60
+ if (!request?.heavy) return ResourceLease.light(request);
61
+ this.ensureRoot();
62
+ const coordinatedRequest = resourceRequestForProject(request, options.cwd);
63
+ const waitMs = Math.max(0, Math.min(30 * 60_000, Number(options.waitMs) || 0));
64
+ const started = performance.now();
65
+ const waiter = createResourceWaiter(this.waitersDir, coordinatedRequest, waitMs, this.now());
66
+ let lastDecision = null;
67
+ let retryAttempt = 0;
68
+ try {
69
+ while (true) {
70
+ const changeSignal = resourceChangeSignal(this);
71
+ abortIfNeeded(options.signal);
72
+ options.cancelCheck?.();
73
+ const host = await this.freshHostSnapshot(options.cwd || process.cwd(), coordinatedRequest);
74
+ const processParents = readdirSync(this.leasesDir).some((name) => LEASE_FILE.test(name)) ? await this.sampleProcessParents() : {};
75
+ const result = await this.withLock(() => {
76
+ const leases = this.pruneAndReadLeases();
77
+ const previous = readResourceHostSample(this.hostSampleFile, { optional: true });
78
+ const enrichedHost = deriveHostRates(host, previous);
79
+ this.writeJson(this.hostSampleFile, enrichedHost);
80
+ const accounting = resourceCoordinatorAccounting(leases, processParents, waiter.owner.pid);
81
+ const pressure = resourcePressureSnapshot(enrichedHost, leases, coordinatedRequest.priority, this.now(), accounting.accounting, coordinatedRequest);
82
+ const effectiveRequest = fitElasticRequestToPressure(coordinatedRequest, pressure);
83
+ if (waiter.request.cpu !== effectiveRequest.cpu || waiter.request.memory_mb !== effectiveRequest.memory_mb || waiter.request.compiler_jobs !== effectiveRequest.compiler_jobs) {
84
+ waiter.request = normalizedRequest(effectiveRequest); this.writeJson(join(this.waitersDir, `wait_${waiter.waiter_id}.json`), waiter);
85
+ }
86
+ const waiters = this.pruneAndReadWaiters();
87
+ const evaluate = resourceCoordinatorEvaluator(this.evaluate, processParents, waiter.owner.pid);
88
+ const decision = evaluate(enrichedHost, leases, effectiveRequest, this.now(), waiter);
89
+ const selected = selectedResourceWaiter(waiters, leases, enrichedHost, evaluate, this.now());
90
+ if (!decision.admitted || selected?.waiter_id !== waiter.waiter_id) {
91
+ return { decision: decision.admitted ? { ...decision, admitted: false, reason: "fairness_wait" } : decision, lease: null };
92
+ }
93
+ const lease = this.createLease(decision.reservation ? { ...effectiveRequest, ...decision.reservation } : effectiveRequest, options.cwd);
94
+ removeResourceWaiter(this.waitersDir, waiter);
95
+ return { decision, lease };
96
+ }, Math.max(1, Math.min(PROCESS_OWNERSHIP_LOCK_WAIT_MS, waitMs - (performance.now() - started))));
97
+ lastDecision = result.decision;
98
+ if (result.lease) return new ResourceLease(this, result.lease, result.lease.request);
99
+ if (!resourceAdmissionDecisionRetryable(lastDecision)) throw new ResourceAdmissionError(lastDecision);
100
+ const elapsed = performance.now() - started;
101
+ if (elapsed >= waitMs) throw new ResourceAdmissionError(lastDecision);
102
+ const remaining = Math.max(1, waitMs - elapsed);
103
+ const delay = resourceRetryDelayMs({
104
+ attempt: retryAttempt, priority: coordinatedRequest.priority,
105
+ reason: lastDecision?.reason, remainingMs: remaining, random: this.random,
106
+ });
107
+ retryAttempt += 1;
108
+ await waitForResourceChange(changeSignal, delay, this.sleep, options.signal);
109
+ }
110
+ } finally {
111
+ if (removeResourceWaiter(this.waitersDir, waiter)) signalResourceChange(this);
112
+ }
113
+ }
114
+ async snapshot(options = {}) {
115
+ this.ensureRoot();
116
+ const sampleRequest = options.full === true ? { resource_class: "mixed" } : { resource_class: "adaptive" };
117
+ const host = await this.freshHostSnapshot(options.cwd || process.cwd(), sampleRequest);
118
+ const processParents = readdirSync(this.leasesDir).some((name) => LEASE_FILE.test(name)) ? await this.sampleProcessParents() : {};
119
+ return this.withLock(() => {
120
+ const leases = this.pruneAndReadLeases();
121
+ const waiters = this.pruneAndReadWaiters();
122
+ const previous = readResourceHostSample(this.hostSampleFile, { optional: true });
123
+ const enrichedHost = deriveHostRates(host, previous);
124
+ this.writeJson(this.hostSampleFile, enrichedHost);
125
+ const accounting = resourceCoordinatorAccounting(leases, processParents); const evaluate = resourceCoordinatorEvaluator(this.evaluate, processParents); const now = this.now();
126
+ return {
127
+ schema_version: SCHEMA,
128
+ healthy: true,
129
+ root_scope: "machine_user",
130
+ admission_model: "work_conserving_multi_resource",
131
+ hard_resource_quota: false,
132
+ host: publicHost(enrichedHost),
133
+ pressure: resourcePressureSnapshot(enrichedHost, leases, options.priority || "ordinary", now, accounting.accounting),
134
+ active_leases: leases.length,
135
+ resources: accounting.resources,
136
+ waiters: { ...resourceWaiterQueueSnapshot(waiters, now), drain_active: resourceWaiterDrainActive(waiters, leases, enrichedHost, evaluate, now) },
137
+ };
138
+ });
139
+ }
140
+
141
+ async bindLease(leaseId, token, child, options = {}) {
142
+ if (!Number.isInteger(child?.pid) || child.pid <= 0) throw new Error("resource lease requires a spawned child pid");
143
+ const observedStart = await processStartTimeMsAsync(child.pid);
144
+ return this.withLock(() => {
145
+ const file = this.leasePath(leaseId);
146
+ const lease = this.readJson(file, 32 * 1024, "resource lease");
147
+ assertLeaseToken(lease, token);
148
+ const processGroupIsolated = options.processGroupIsolated === true && process.platform !== "win32";
149
+ const processGroupId = processGroupIsolated ? child.pid : null;
150
+ if (lease.owner.kind === "process") {
151
+ if (lease.owner.pid !== child.pid
152
+ || lease.owner.process_group_isolated !== processGroupIsolated
153
+ || lease.owner.process_group_id !== processGroupId) {
154
+ throw new Error("resource lease is already bound with different process ownership semantics");
155
+ }
156
+ return lease;
157
+ }
158
+ const nowIso = new Date(this.now()).toISOString();
159
+ lease.owner = {
160
+ kind: "process",
161
+ pid: child.pid,
162
+ process_started_at: Number.isFinite(observedStart) ? new Date(observedStart).toISOString() : nowIso,
163
+ process_group_id: processGroupId,
164
+ process_group_isolated: processGroupIsolated,
165
+ };
166
+ lease.bound_at = nowIso;
167
+ this.writeJson(file, lease);
168
+ return lease;
169
+ }, PROCESS_OWNERSHIP_LOCK_WAIT_MS);
170
+ }
171
+
172
+ async releaseLease(leaseId, token) {
173
+ if (!leaseId || !token) return false;
174
+ this.ensureRoot();
175
+ return this.withLock(() => {
176
+ const file = this.leasePath(leaseId);
177
+ const lease = this.readJson(file, 32 * 1024, "resource lease", true);
178
+ if (!lease) return false;
179
+ assertLeaseToken(lease, token);
180
+ const owner = lease.owner; const ownerStatus = owner?.kind === "process" ? inspectLeaseOwnerProcess(lease) : null;
181
+ if (owner?.kind === "process" && owner.process_group_isolated === true && ownerStatus?.reason !== "pid_reused" && isProcessGroupAlive(owner.process_group_id)) return true;
182
+ const removed = removeOwnedJsonFileSync(file, { lease_id: leaseId, token }, { maxBytes: 32 * 1024 });
183
+ if (!removed) throw new Error("resource lease changed before release; state may require inspection");
184
+ signalResourceChange(this);
185
+ return true;
186
+ }, PROCESS_OWNERSHIP_LOCK_WAIT_MS);
187
+ }
188
+ ensureRoot() {
189
+ if (this.ready) return;
190
+ ensureOwnerOnlyDirectorySync(this.root);
191
+ ensureOwnerOnlyDirectorySync(this.leasesDir);
192
+ ensureOwnerOnlyDirectorySync(this.waitersDir);
193
+ const marker = this.readJson(this.markerFile, 4096, "resource coordinator protocol", true);
194
+ if (!marker) {
195
+ try { createExclusiveFileSync(this.markerFile, `${JSON.stringify({ schema_version: SCHEMA, protocol: "agent-resource-coordinator" })}\n`, { mode: 0o600 }); }
196
+ catch (error) { if (error?.code !== "EEXIST") throw error; }
197
+ }
198
+ const current = this.readJson(this.markerFile, 4096, "resource coordinator protocol");
199
+ if (current?.schema_version !== SCHEMA || current?.protocol !== "agent-resource-coordinator") throw new Error("unsupported resource coordinator protocol");
200
+ this.ready = true;
201
+ }
202
+ async freshHostSnapshot(cwd, request = null) {
203
+ const scope = resourceProjectHash(cwd);
204
+ const needsIo = resourceHostNeedsFreshIo(request);
205
+ const pending = this.hostSamplesInFlight.get(scope);
206
+ if (pending) {
207
+ const shared = await pending.promise;
208
+ if (!needsIo || shared?.io_sampled === true) return shared;
209
+ }
210
+ const flight = { promise: freshResourceHostSnapshot({
211
+ cached: readResourceHostSample(this.hostSampleFile, { optional: true }),
212
+ current: this.now(), sampleHost: this.sampleHost, cwd, request, scope,
213
+ }) };
214
+ this.hostSamplesInFlight.set(scope, flight);
215
+ try { return await flight.promise; }
216
+ finally { if (this.hostSamplesInFlight.get(scope) === flight) this.hostSamplesInFlight.delete(scope); }
217
+ }
218
+ async withLock(callback, timeoutMs = undefined) {
219
+ this.ensureRoot();
220
+ for (let attempt = 1; attempt <= 4; attempt += 1) {
221
+ try { return await withResourceTransactionLock(this.root, callback, { now: this.now, sleep: this.sleep, random: this.random, timeoutMs }); }
222
+ catch (error) { if (error?.code !== RESOURCE_STAGING_BUSY_CODE || attempt === 4) throw error; await this.sleep(5); }
223
+ }
224
+ }
225
+ pruneAndReadWaiters() {
226
+ let entries = readdirSync(this.waitersDir, { withFileTypes: true });
227
+ if (recoverResourceDirectoryStaging(this.waitersDir, entries, "wait")) entries = readdirSync(this.waitersDir, { withFileTypes: true });
228
+ const waiters = pruneAndReadResourceWaiters(this.waitersDir, entries, this.now()); if (waiters.length < entries.length) signalResourceChange(this); return waiters;
229
+ }
230
+ pruneAndReadLeases() {
231
+ const leases = []; let pruned = false;
232
+ let entries = readdirSync(this.leasesDir, { withFileTypes: true });
233
+ if (recoverResourceDirectoryStaging(this.leasesDir, entries, "lease")) entries = readdirSync(this.leasesDir, { withFileTypes: true });
234
+ for (const entry of entries) {
235
+ if (!entry.isFile() || !LEASE_FILE.test(entry.name)) throw new Error("resource coordinator lease directory contains an unexpected entry");
236
+ const file = join(this.leasesDir, entry.name);
237
+ const lease = this.readJson(file, 32 * 1024, "resource lease");
238
+ validateLease(lease);
239
+ if (this.leaseIsStale(lease)) {
240
+ if (!removeOwnedJsonFileSync(file, { lease_id: lease.lease_id, token: lease.token }, { maxBytes: 32 * 1024 })) {
241
+ throw new Error("resource lease changed during stale pruning");
242
+ }
243
+ pruned = true; continue;
244
+ }
245
+ leases.push(lease);
246
+ }
247
+ if (pruned) signalResourceChange(this); return leases;
248
+ }
249
+
250
+ leaseIsStale(lease) {
251
+ const owner = lease.owner; const status = inspectLeaseOwnerProcess(lease);
252
+ if (owner.kind === "process" && status.reason === "pid_reused") return true;
253
+ if (owner.kind === "process" && owner.process_group_isolated === true && isProcessGroupAlive(owner.process_group_id)) return false;
254
+ if (owner.kind === "provisional" && this.now() - Date.parse(lease.acquired_at) > PROVISIONAL_TTL_MS) return true;
255
+ return status.reclaimable === true;
256
+ }
257
+
258
+ createLease(request, cwd) {
259
+ const id = randomBytes(16).toString("hex"); const token = randomBytes(32).toString("hex");
260
+ const nowIso = new Date(this.now()).toISOString();
261
+ const lease = {
262
+ schema_version: SCHEMA,
263
+ lease_id: id,
264
+ token,
265
+ acquired_at: nowIso,
266
+ bound_at: null,
267
+ project_hash: resourceProjectHash(cwd || process.cwd()),
268
+ owner: {
269
+ kind: "provisional",
270
+ pid: process.pid,
271
+ process_started_at: new Date(currentProcessStartTimeMs()).toISOString(),
272
+ },
273
+ request: normalizedRequest(request),
274
+ };
275
+ createExclusiveFileSync(this.leasePath(id), `${JSON.stringify(lease)}\n`, { mode: 0o600 });
276
+ return lease;
277
+ }
278
+
279
+ leasePath(id) {
280
+ if (!/^[a-f0-9]{32}$/.test(String(id || ""))) throw new Error("invalid resource lease id");
281
+ return join(this.leasesDir, `lease_${id}.json`);
282
+ }
283
+ readJson(file, maxBytes, label, optional = false) {
284
+ try {
285
+ const text = readBoundedRegularFileSync(file, maxBytes, label, { verifyPathIdentity: true, rejectMultipleLinks: true }).toString("utf8");
286
+ const value = JSON.parse(text);
287
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
288
+ } catch (error) {
289
+ if (optional && (error?.code === "ENOENT" || error?.cause?.code === "ENOENT")) return null;
290
+ throw error;
291
+ }
292
+ }
293
+ writeJson(file, value) { replaceFileAtomicallySync(file, `${JSON.stringify(value)}\n`, { mode: 0o600 }); }
294
+ }
295
+
296
+ export class ResourceLease {
297
+ constructor(coordinator, lease, request) { this.coordinator = coordinator; this.id = lease.lease_id; this.token = lease.token; this.request = request; this.released = false; }
298
+ static light(request) { return new ResourceLease(null, { lease_id: null, token: null }, request); }
299
+ get active() { return Boolean(this.coordinator && this.id && !this.released); }
300
+ async bindProcess(child, options = {}) { if (this.active) await this.coordinator.bindLease(this.id, this.token, child, options); return this; }
301
+ async release() {
302
+ if (!this.active) return false;
303
+ const released = await this.coordinator.releaseLease(this.id, this.token);
304
+ this.released = true;
305
+ return released;
306
+ }
307
+ }
308
+
309
+ function normalizedRequest(request) {
310
+ const allowed = ["family", "resource_class", "priority", "cpu", "io", "memory_mb", "disk_reserve_bytes", "heavy", "compiler_jobs", "unbounded", "serialize_project", "contention_key"];
311
+ return Object.fromEntries(allowed.map((key) => [key, request[key] ?? null]));
312
+ }
313
+ function validateLease(lease) {
314
+ if (lease?.schema_version !== SCHEMA || !/^[a-f0-9]{32}$/.test(String(lease.lease_id || "")) || !/^[a-f0-9]{64}$/.test(String(lease.token || ""))) throw new Error("resource coordinator lease is invalid");
315
+ if (!Number.isFinite(Date.parse(String(lease.acquired_at || ""))) || !validOwner(lease.owner)) throw new Error("resource coordinator lease ownership is invalid");
316
+ validateResourceRequest(lease.request);
317
+ if (lease.owner.kind === "process") {
318
+ if (!Number.isFinite(Date.parse(String(lease.bound_at || ""))) || typeof lease.owner.process_group_isolated !== "boolean") throw new Error("resource coordinator process lease is invalid");
319
+ if (lease.owner.process_group_isolated && lease.owner.process_group_id !== lease.owner.pid) throw new Error("resource coordinator process group is invalid");
320
+ if (!lease.owner.process_group_isolated && lease.owner.process_group_id !== null) throw new Error("resource coordinator process group is invalid");
321
+ } else if (lease.bound_at !== null) throw new Error("resource coordinator provisional lease is invalid");
322
+ }
323
+ function validOwner(owner) { return ["provisional", "process"].includes(owner?.kind) && Number.isInteger(owner?.pid) && owner.pid > 0 && Number.isFinite(Date.parse(String(owner.process_started_at || ""))); }
324
+ function assertLeaseToken(lease, token) { validateLease(lease); if (lease.token !== token) throw new Error("resource lease ownership changed"); }
325
+ function inspectLeaseOwnerProcess(lease) { return inspectProcessInstance({ pid: lease.owner.pid, startedAt: lease.acquired_at, processStartedAt: lease.owner.process_started_at }); }
326
+ function isProcessGroupAlive(value) {
327
+ const pgid = Number(value); if (process.platform === "win32" || !Number.isInteger(pgid) || pgid <= 0) return false;
328
+ try { process.kill(-pgid, 0); return true; } catch (error) { return error?.code === "EPERM"; }
329
+ }
330
+ function publicHost(host) {
331
+ const {
332
+ pageouts_total: _pageouts, swapouts_total: _swapouts,
333
+ cpu_time_ms_total: _cpuTime, cpu_idle_ms_total: _cpuIdle,
334
+ sample_scope: _sampleScope,
335
+ ...value
336
+ } = host || {};
337
+ return value;
338
+ }
339
+ function abortIfNeeded(signal) { if (signal?.aborted) throw signal.reason instanceof Error ? signal.reason : new Error("resource admission cancelled"); }
@@ -0,0 +1,83 @@
1
+ import { homedir } from "node:os";
2
+ import { basename, join, resolve } from "node:path";
3
+ import { createExclusiveFileSync } from "./exclusive-file.mjs";
4
+ import { preservesCompilerJobs } from "./resource-elastic-request.mjs";
5
+ import { makeCommandJobPlan } from "./resource-make-concurrency.mjs";
6
+ import { ensureOwnerOnlyDirectorySync, ownerOnlyFile } from "./secure-file.mjs";
7
+ import { resourceProjectHash } from "./resource-project-key.mjs";
8
+
9
+ export function defaultAgentBuildRoot(env = process.env) {
10
+ if (env.AGENT_BUILD_ROOT) return resolve(String(env.AGENT_BUILD_ROOT));
11
+ if (process.platform === "darwin") return join(homedir(), "Library", "Caches", "AgentBuilds.noindex");
12
+ if (process.platform === "win32") return resolve(env.LOCALAPPDATA || env.APPDATA || homedir(), "AgentBuilds");
13
+ return join(env.XDG_CACHE_HOME ? resolve(String(env.XDG_CACHE_HOME)) : join(homedir(), ".cache"), "agent-builds");
14
+ }
15
+
16
+ export function prepareResourceBuildCommand(command, args, environment, request, cwd, options = {}) {
17
+ const env = { ...environment };
18
+ const argv = args.map(String);
19
+ if (!request?.heavy) return { command, args: argv, environment: env };
20
+ const base = basename(String(command || "")).toLowerCase();
21
+ applyCompilerConcurrency(argv, base, request);
22
+ if (!buildRootRelevant(request.family)) return { command, args: argv, environment: env };
23
+ const root = ensureBuildRoot(options.root || defaultAgentBuildRoot(options.env));
24
+ const project = projectBuildHash(cwd);
25
+ env.AGENT_BUILD_ROOT ||= root;
26
+
27
+ if (request.family === "cargo") {
28
+ const target = ensureProjectBuildDir(root, "cargo", project);
29
+ if (!env.CARGO_TARGET_DIR && !hasOption(argv, "--target-dir")) env.CARGO_TARGET_DIR = target;
30
+ } else if (request.family === "swift" && base === "swift") {
31
+ const scratch = ensureProjectBuildDir(root, "swift", project);
32
+ if (!hasOption(argv, "--scratch-path")) insertSwiftOption(argv, "--scratch-path", scratch);
33
+ if (request.compiler_jobs && !hasAnyOption(argv, ["-j", "--jobs"])) insertSwiftOption(argv, "-j", String(request.compiler_jobs));
34
+ } else if (request.family === "xcodebuild" && base === "xcodebuild") {
35
+ const derived = ensureProjectBuildDir(root, "xcode", project);
36
+ if (!hasOption(argv, "-derivedDataPath")) argv.unshift("-derivedDataPath", derived);
37
+ if (request.compiler_jobs && !hasOption(argv, "-jobs")) argv.unshift("-jobs", String(request.compiler_jobs));
38
+ }
39
+ return { command, args: argv, environment: env };
40
+ }
41
+
42
+ export function projectBuildHash(cwd) {
43
+ return resourceProjectHash(cwd || process.cwd());
44
+ }
45
+
46
+ function ensureBuildRoot(rootInput) {
47
+ const root = resolve(rootInput);
48
+ ensureOwnerOnlyDirectorySync(root);
49
+ if (process.platform === "darwin") ensureNoIndexMarker(root);
50
+ return root;
51
+ }
52
+ function ensureProjectBuildDir(root, family, project) {
53
+ const dir = join(root, family, project);
54
+ ensureOwnerOnlyDirectorySync(dir);
55
+ return dir;
56
+ }
57
+ function ensureNoIndexMarker(root) {
58
+ const marker = join(root, ".metadata_never_index");
59
+ try { createExclusiveFileSync(marker, "", { mode: 0o600 }); }
60
+ catch (error) { if (error?.code !== "EEXIST") throw error; }
61
+ ownerOnlyFile(marker);
62
+ }
63
+ function buildRootRelevant(family) { return ["cargo", "swift", "xcodebuild"].includes(String(family || "")); }
64
+ function hasOption(argv, option) { return argv.some((value) => value === option || value.startsWith(`${option}=`)); }
65
+ function hasAnyOption(argv, options) { return options.some((option) => hasOption(argv, option) || (option === "-j" && argv.some((value) => /^-j\d+$/.test(value)))); }
66
+ function insertSwiftOption(argv, flag, value) {
67
+ const index = argv.length && ["build", "test", "package"].includes(argv[0]) ? 1 : 0;
68
+ argv.splice(index, 0, flag, value);
69
+ }
70
+ function applyCompilerConcurrency(argv, base, request) {
71
+ const jobs = request?.compiler_jobs;
72
+ if (!jobs || request.family !== "generic-build" || preservesCompilerJobs(request)) return;
73
+ if (base === "make" && !makeCommandJobPlan([base, ...argv])) argv.unshift("-j", String(jobs));
74
+ else if (base === "ninja" && !hasAnyOption(argv, ["-j", "--jobs"])) argv.unshift("-j", String(jobs));
75
+ else if (base === "cmake" && !hasCmakeParallelOption(argv)) insertBeforeSeparator(argv, "--parallel", String(jobs));
76
+ else if (["gradle", "gradlew"].includes(base) && !hasOption(argv, "--max-workers")) argv.unshift("--max-workers", String(jobs));
77
+ else if (["mvn", "mvnw"].includes(base) && !hasCompactOrLongOption(argv, "-T", "--threads")) argv.unshift("-T", String(jobs));
78
+ else if (base === "go" && !hasGoParallelOption(argv) && ["build", "test"].includes(argv[0])) argv.splice(1, 0, "-p", String(jobs));
79
+ }
80
+ function hasCompactOrLongOption(argv, short, long) { return hasOption(argv, long) || argv.some((value) => value === short || value.startsWith(`${short}=`) || value.startsWith(short) && value.length > short.length); }
81
+ function hasCmakeParallelOption(argv) { const end = argv.indexOf("--"); return argv.slice(0, end < 0 ? argv.length : end).some((value) => value.startsWith("-j") || value.startsWith("--parallel")); }
82
+ function hasGoParallelOption(argv) { return argv.some((value) => ["-p", "--p"].includes(value) || /^--?p=/.test(value)); }
83
+ function insertBeforeSeparator(argv, flag, value) { const index = argv.indexOf("--"); argv.splice(index < 0 ? argv.length : index, 0, flag, value); }
@@ -0,0 +1,38 @@
1
+ import { availableParallelism } from "node:os";
2
+
3
+ export function cargoExplicitJobPlan(argv, environment = {}, maximum = 1024) {
4
+ const declared = cargoDeclaredJobPlan(argv, maximum);
5
+ if (declared) return declared;
6
+ if (!Object.prototype.hasOwnProperty.call(environment || {}, "CARGO_BUILD_JOBS")) return null;
7
+ return cargoJobValuePlan(environment.CARGO_BUILD_JOBS, maximum, true);
8
+ }
9
+
10
+ function cargoDeclaredJobPlan(argv, maximum) {
11
+ let plan = null;
12
+ for (let index = 1; index < (argv || []).length; index += 1) {
13
+ const current = String(argv[index]);
14
+ if (current === "--") break;
15
+ if (["-j", "--jobs"].includes(current)) {
16
+ plan = cargoJobValuePlan(argv[index + 1], maximum, false); index += 1; continue;
17
+ }
18
+ if (current.startsWith("--jobs=")) { plan = cargoJobValuePlan(current.slice("--jobs=".length), maximum, false); continue; }
19
+ if (current.startsWith("-j") && current.length > 2) plan = cargoJobValuePlan(current.slice(2), maximum, false);
20
+ }
21
+ return plan;
22
+ }
23
+
24
+ function cargoJobValuePlan(value, maximum, preserve) {
25
+ const text = String(value ?? "").trim();
26
+ const cores = availableParallelism();
27
+ if (text === "default") return numericPlan(cores, maximum, preserve);
28
+ if (!/^-?\d+$/.test(text)) return unboundedPlan(preserve);
29
+ const parsed = Number(text);
30
+ if (!Number.isSafeInteger(parsed) || parsed === 0) return unboundedPlan(preserve);
31
+ const jobs = parsed < 0 ? cores + parsed : parsed;
32
+ return numericPlan(jobs, maximum, preserve);
33
+ }
34
+ function numericPlan(jobs, maximum, preserve) {
35
+ return Number.isSafeInteger(jobs) && jobs >= 1 && jobs <= maximum
36
+ ? { jobs, elastic: false, unbounded: false, preserve } : unboundedPlan(preserve);
37
+ }
38
+ function unboundedPlan(preserve) { return { jobs: null, elastic: false, unbounded: true, preserve }; }