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
@@ -2,29 +2,24 @@ import { spawn } from "node:child_process";
2
2
  import { basename } from "node:path";
3
3
  import { stat } from "node:fs/promises";
4
4
  import { BoundedOutput } from "./bounded-output.mjs";
5
+ import { attachChildProcessSettlement } from "./child-process-settlement.mjs";
5
6
  import { executionEnv, workspaceShellCommand } from "./shell.mjs";
6
7
  import { MAX_COMMAND_BYTES, validateArgv } from "./process-contract.mjs";
7
8
  import { terminateProcessTreeWithEscalation } from "./process-tree.mjs";
8
9
  import { BridgeError } from "./errors.mjs";
9
- import { clampInteger } from "./numbers.mjs";
10
+ import { delegatedProcessCommand } from "./delegated-process-sandbox.mjs";
10
11
  import { ProcessOutputStream } from "./process-output-stream.mjs";
12
+ import { processForegroundTimeoutSeconds, registeredCommandTimeoutSeconds } from "./process-foreground-timeout.mjs";
11
13
  import { processFailureMessage, publicProcessToolResult } from "./process-result-projection.mjs";
12
- import {
13
- DEFAULT_PROCESS_OUTPUT_BYTES,
14
- MAX_PROCESS_SESSION_OUTPUT_BYTES,
15
- MAX_PROCESS_STDIN_BYTES,
16
- MAX_PROCESS_TIMEOUT_SECONDS,
17
- MIN_PROCESS_TIMEOUT_SECONDS,
18
- PROCESS_SESSION_RETENTION_MS,
19
- PUBLIC_PROCESS_INLINE_OUTPUT_BYTES,
20
- } from "./execution-limits.mjs";
21
-
22
- const PROCESS_OUTPUT_CAPTURE = Symbol("process-output-capture");
23
- const CONTINUATION_READ_BYTES = 64 * 1024;
24
-
14
+ import { processCancellationFailure, processChildErrorFailure, processPostSpawnFailure, processPreSpawnFailure, processTimeoutFailure } from "./process-nonreplayable-settlement.mjs";
15
+ import { acquireProcessResources, bindProcessResources, releaseProcessResources, releaseProcessResourcesQuietly } from "./resource-process-admission.mjs";
16
+ import { foregroundResourceWaitMs } from "./resource-foreground-wait.mjs";
17
+ import { validateFixedProcessEnvironment } from "./fixed-process-environment.mjs";
18
+ import { EXECUTION_SURFACE, withExecutionSurface } from "./execution-surface.mjs";
19
+ import { DEFAULT_PROCESS_OUTPUT_BYTES, MAX_PROCESS_SESSION_OUTPUT_BYTES, MAX_PROCESS_STDIN_BYTES, PROCESS_SESSION_RETENTION_MS, PUBLIC_PROCESS_INLINE_OUTPUT_BYTES } from "./execution-limits.mjs";
20
+ const PROCESS_OUTPUT_CAPTURE = Symbol("process-output-capture"); const CONTINUATION_READ_BYTES = 64 * 1024;
25
21
  function spawnDirectProcess(command, args, options) {
26
- // Keep the production child_process API call structurally separate from the
27
- // injectable test seam and enforce non-shell execution at the final boundary.
22
+ // Keep the production child_process API separate from the injectable test seam and enforce non-shell execution.
28
23
  return spawn(command, args, {
29
24
  cwd: options.cwd,
30
25
  env: options.env,
@@ -33,55 +28,53 @@ function spawnDirectProcess(command, args, options) {
33
28
  shell: false,
34
29
  });
35
30
  }
36
-
37
31
  export class ProcessExecutionService {
38
- constructor({ workspace, policy, policyGate, runtimeDir, processTracker, resolveExistingPath, resolveLocalCommand, displayPath, throwIfCancelled, retainCompletedOutput = null, spawnProcess = spawnDirectProcess, terminateProcess = terminateProcessTreeWithEscalation }) {
32
+ constructor({ workspace, policy, policyGate, policyForContext = null, runtimeDir, processTracker, resourceCoordinator = null, resourceWaitMs = undefined, resolveExistingPath, resolveLocalCommand, displayPath, throwIfCancelled, retainCompletedOutput = null, spawnProcess = spawnDirectProcess, terminateProcess = terminateProcessTreeWithEscalation, childSettlementOptions = {} }) {
39
33
  this.workspace = workspace;
40
34
  this.policy = policy;
41
35
  this.policyGate = policyGate;
36
+ this.policyForContext = typeof policyForContext === "function" ? policyForContext : () => this.policy;
42
37
  this.runtimeDir = runtimeDir;
43
38
  this.processTracker = processTracker;
39
+ this.resourceCoordinator = resourceCoordinator;
40
+ this.resourceWaitMs = resourceWaitMs;
44
41
  this.resolveExistingPath = resolveExistingPath;
45
42
  this.resolveLocalCommand = resolveLocalCommand;
46
43
  this.displayPath = displayPath;
47
44
  this.throwIfCancelled = throwIfCancelled;
48
45
  this.retainCompletedOutput = typeof retainCompletedOutput === "function" ? retainCompletedOutput : null;
49
- this.spawnProcess = spawnProcess;
50
- this.terminateProcess = terminateProcess;
46
+ this.spawnProcess = spawnProcess; this.terminateProcess = terminateProcess; this.childSettlementOptions = childSettlementOptions;
51
47
  }
52
-
53
48
  async runDirect(args, context = {}) {
54
49
  this.policyGate.assert("run_process");
55
50
  const argv = validateArgv(args.argv);
56
- const cwd = await this.resolveExistingPath(args.cwd || ".");
51
+ const cwd = await this.resolveExistingPath(args.cwd || ".", context);
57
52
  if (!(await stat(cwd)).isDirectory()) throw new BridgeError("invalid_request", "cwd is not a directory");
53
+ const timeoutSeconds = processForegroundTimeoutSeconds("run_process", args.timeout_seconds, context);
58
54
  const result = await this.runPublic(
59
- argv[0], argv.slice(1),
60
- clampInteger(args.timeout_seconds, 120, MIN_PROCESS_TIMEOUT_SECONDS, MAX_PROCESS_TIMEOUT_SECONDS) * 1000,
55
+ argv[0], argv.slice(1), timeoutSeconds * 1000,
61
56
  context, cwd,
62
57
  );
63
58
  return publicProcessToolResult(result);
64
59
  }
65
-
66
60
  async runRegistered(args, context = {}) {
67
61
  this.policyGate.assert("run_local_command");
68
62
  const command = await this.resolveLocalCommand(args, context);
69
63
  const argv = validateArgv(command.argv);
70
- const cwd = await this.resolveExistingPath(command.cwd);
64
+ const cwd = await this.resolveExistingPath(command.cwd, context);
71
65
  if (!(await stat(cwd)).isDirectory()) throw new BridgeError("invalid_request", "registered command cwd is not a directory");
72
- const requested = args.timeout_seconds === undefined
73
- ? command.timeoutSeconds
74
- : clampInteger(args.timeout_seconds, command.timeoutSeconds, MIN_PROCESS_TIMEOUT_SECONDS, MAX_PROCESS_TIMEOUT_SECONDS);
75
- const timeoutSeconds = Math.min(requested, command.timeoutSeconds);
66
+ const timeoutSeconds = registeredCommandTimeoutSeconds(args, command, context);
76
67
  const result = await this.runPublic(argv[0], argv.slice(1), timeoutSeconds * 1000, context, cwd);
77
- return publicProcessToolResult({ name: command.name, cwd: this.displayPath(cwd), timeout_seconds: timeoutSeconds, ...result });
68
+ return publicProcessToolResult({ name: command.name, cwd: this.displayPath(cwd, context), timeout_seconds: timeoutSeconds, ...result });
78
69
  }
79
-
80
- async probeShell(context = {}) {
70
+ async probeShell(context = {}, timeoutMs = 5_000) {
81
71
  const shell = workspaceShellCommand(process.platform === "win32" ? "cd" : "pwd");
82
- return this.run(shell.cmd, shell.args, 5000, true, 64 * 1024, context);
72
+ return this.run(shell.cmd, shell.args, timeoutMs, true, 64 * 1024, context, this.workspace, null, { internalFixed: true });
73
+ }
74
+ async runFixedInternal(cmd, args, timeoutMs, allowFailure = false, maxOutputBytes = DEFAULT_PROCESS_OUTPUT_BYTES, context = {}, cwd = this.workspace, stdin = null, environment = {}) {
75
+ const argv = validateArgv([cmd, ...args]);
76
+ return this.run(argv[0], argv.slice(1), timeoutMs, allowFailure, maxOutputBytes, context, cwd, stdin, { internalFixed: true, internalEnvironment: validateFixedProcessEnvironment(environment) });
83
77
  }
84
-
85
78
  async runShell(command, timeoutSeconds, context = {}) {
86
79
  this.policyGate.assert("exec_command");
87
80
  if (!command || typeof command !== "string") throw new BridgeError("invalid_request", "command is required");
@@ -90,16 +83,14 @@ export class ProcessExecutionService {
90
83
  const shell = workspaceShellCommand(command);
91
84
  const result = await this.runPublic(
92
85
  shell.cmd, shell.args,
93
- clampInteger(timeoutSeconds, 120, MIN_PROCESS_TIMEOUT_SECONDS, MAX_PROCESS_TIMEOUT_SECONDS) * 1000,
86
+ processForegroundTimeoutSeconds("exec_command", timeoutSeconds, context) * 1000,
94
87
  context, this.workspace,
95
88
  );
96
89
  return publicProcessToolResult(result);
97
90
  }
98
-
99
91
  terminateAll(signal = "SIGTERM", escalate = false) {
100
92
  this.processTracker.terminateAll(signal, escalate);
101
93
  }
102
-
103
94
  async runPublic(cmd, args, timeoutMs, context, cwd) {
104
95
  const startedAt = Date.now();
105
96
  const result = await this.run(
@@ -114,7 +105,7 @@ export class ProcessExecutionService {
114
105
  command: basename(cmd), cwd,
115
106
  stdout: capture.stdout, stderr: capture.stderr,
116
107
  exitCode: result.code, startedAt, closedAt: Date.now(),
117
- });
108
+ }, context);
118
109
  if (session) {
119
110
  continuation = {
120
111
  output_session_id: session.session_id,
@@ -141,13 +132,22 @@ export class ProcessExecutionService {
141
132
  }
142
133
  return publicResult;
143
134
  }
144
-
145
135
  async run(cmd, args, timeoutMs, allowFailure = false, maxOutputBytes = DEFAULT_PROCESS_OUTPUT_BYTES, context = {}, cwd = this.workspace, stdin = null, options = {}) {
146
136
  this.throwIfCancelled(context);
147
137
  if (stdin !== null && Buffer.byteLength(String(stdin)) > MAX_PROCESS_STDIN_BYTES) {
148
138
  throw new BridgeError("limit_exceeded", "process stdin exceeds 1 MiB");
149
139
  }
150
-
140
+ const internalFixed = options.internalFixed === true;
141
+ const baseEnvironment = executionEnv(this.workspace, {
142
+ fullEnv: internalFixed ? false : this.policyForContext(context).minimalEnv === false, runtimeDir: this.runtimeDir,
143
+ });
144
+ if (internalFixed) Object.assign(baseEnvironment, options.internalEnvironment || {});
145
+ const executionEnvironment = internalFixed ? baseEnvironment : withExecutionSurface(baseEnvironment, EXECUTION_SURFACE.foregroundProcess);
146
+ const admitted = internalFixed || !this.resourceCoordinator
147
+ ? { lease: null, environment: executionEnvironment, command: cmd, args }
148
+ : await acquireProcessResources(this.resourceCoordinator, cmd, args, executionEnvironment, {
149
+ cwd, priority: options.resourcePriority || "interactive", waitMs: options.resourceWaitMs ?? foregroundResourceWaitMs(timeoutMs, this.resourceWaitMs), signal: context.signal,
150
+ });
151
151
  return new Promise((resolvePromise, rejectPromise) => {
152
152
  const stdout = new BoundedOutput(maxOutputBytes);
153
153
  const stderr = new BoundedOutput(maxOutputBytes);
@@ -155,62 +155,64 @@ export class ProcessExecutionService {
155
155
  const retainedStderr = options.retainOutput ? new ProcessOutputStream(MAX_PROCESS_SESSION_OUTPUT_BYTES) : null;
156
156
  let child;
157
157
  try {
158
- child = this.spawnProcess(cmd, args, {
158
+ const launch = internalFixed
159
+ ? { command: cmd, args }
160
+ : delegatedProcessCommand({ command: admitted.command, args: admitted.args, workspace: this.workspace, runtimeDir: this.runtimeDir, context });
161
+ child = this.spawnProcess(launch.command, launch.args, {
159
162
  cwd,
160
- env: executionEnv(this.workspace, { fullEnv: this.policy.minimalEnv === false, runtimeDir: this.runtimeDir }),
163
+ env: admitted.environment,
161
164
  detached: process.platform !== "win32",
162
165
  windowsHide: true,
163
166
  shell: false,
164
167
  });
165
168
  } catch (error) {
166
- rejectPromise(error);
169
+ void releaseProcessResourcesQuietly(admitted.lease).then(() => rejectPromise(
170
+ processPreSpawnFailure(error, options.nonReplayableMutation === true),
171
+ ));
167
172
  return;
168
173
  }
169
-
170
174
  this.processTracker.track(child, context.callId);
175
+ let resourceBindError = null; let childError = null;
176
+ const resourceBinding = bindProcessResources(admitted.lease, child).catch((error) => { resourceBindError = error; this.terminateProcess(child); });
171
177
  if (stdin !== null) {
172
178
  child.stdin?.on?.("error", () => {});
173
179
  child.stdin?.end?.(String(stdin));
174
180
  }
175
-
176
181
  let settled = false;
177
182
  let processClosed = false;
178
183
  let terminationTimer = null;
179
184
  let timeoutTimer = null;
180
185
  const signal = context.signal;
181
-
182
- const cleanupAfterClose = () => {
186
+ const nonReplayableMutation = options.nonReplayableMutation === true;
187
+ const cleanupAfterClose = async () => {
183
188
  if (processClosed) return;
184
189
  processClosed = true;
185
190
  clearTimeout(timeoutTimer);
186
191
  signal?.removeEventListener?.("abort", onAbort);
187
192
  this.processTracker.untrack(child);
193
+ await resourceBinding;
194
+ await releaseProcessResources(admitted.lease);
188
195
  };
189
-
190
196
  const settle = (callback) => {
191
197
  if (settled) return false;
192
198
  settled = true;
193
199
  callback();
194
200
  return true;
195
201
  };
196
-
197
202
  const terminate = () => {
198
203
  if (terminationTimer || processClosed) return;
199
204
  terminationTimer = this.terminateProcess(child);
200
205
  };
201
-
202
206
  const rejectCancelled = () => {
203
207
  terminate();
204
- const reason = signal?.reason;
205
- settle(() => rejectPromise(reason instanceof Error ? reason : new BridgeError("cancelled", "tool call cancelled")));
208
+ settle(() => rejectPromise(processCancellationFailure(nonReplayableMutation, signal)));
206
209
  };
207
-
208
210
  const onAbort = () => rejectCancelled();
209
211
  signal?.addEventListener?.("abort", onAbort, { once: true });
210
212
 
211
213
  timeoutTimer = setTimeout(() => {
212
214
  terminate();
213
- settle(() => rejectPromise(new BridgeError("timeout", `command timed out after ${timeoutMs}ms`, { retryable: true })));
215
+ settle(() => rejectPromise(processTimeoutFailure(nonReplayableMutation, timeoutMs)));
214
216
  }, timeoutMs);
215
217
  timeoutTimer.unref?.();
216
218
 
@@ -223,31 +225,42 @@ export class ProcessExecutionService {
223
225
  retainedStderr?.append(chunk);
224
226
  });
225
227
 
226
- child.on("error", (error) => {
227
- cleanupAfterClose();
228
- settle(() => {
229
- if (allowFailure) resolvePromise(processResult(127, stdout, error.message || stderr.text(), retainedStdout, retainedStderr));
230
- else rejectPromise(error);
231
- });
232
- });
233
-
234
- child.on("close", (code) => {
235
- cleanupAfterClose();
236
- if (settled) return;
237
- try { this.throwIfCancelled(context); } catch (error) {
238
- settle(() => rejectPromise(error));
239
- return;
240
- }
241
- const result = processResult(code, stdout, stderr, retainedStdout, retainedStderr);
242
- if (code === 0 || allowFailure) settle(() => resolvePromise(result));
243
- else settle(() => rejectPromise(new BridgeError("execution_failed", processFailureMessage(result), { details: { process: result } })));
228
+ child.on("error", (error) => { childError ||= error; });
229
+ attachChildProcessSettlement(child, {
230
+ ...this.childSettlementOptions,
231
+ onSettle: (code) => {
232
+ void cleanupAfterClose().then(() => {
233
+ if (settled) return;
234
+ if (resourceBindError) {
235
+ settle(() => rejectPromise(processPostSpawnFailure(nonReplayableMutation, "resource_binding", resourceBindError)));
236
+ return;
237
+ }
238
+ if (childError) {
239
+ const failure = processResult(127, stdout, childError.message || stderr.text(), retainedStdout, retainedStderr);
240
+ settle(() => {
241
+ if (nonReplayableMutation) rejectPromise(processChildErrorFailure(true, childError, Boolean(child?.pid)));
242
+ else if (allowFailure) resolvePromise(failure);
243
+ else rejectPromise(childError);
244
+ });
245
+ return;
246
+ }
247
+ try { this.throwIfCancelled(context); } catch (error) {
248
+ const trigger = signal?.reason instanceof BridgeError && signal.reason.code === "timeout" ? "timeout" : "cancelled";
249
+ settle(() => rejectPromise(processPostSpawnFailure(nonReplayableMutation, trigger, error)));
250
+ return;
251
+ }
252
+ const result = processResult(code, stdout, stderr, retainedStdout, retainedStderr);
253
+ if (code === 0 || allowFailure) settle(() => resolvePromise(result));
254
+ else settle(() => rejectPromise(processPostSpawnFailure(nonReplayableMutation, "nonzero_exit",
255
+ new BridgeError("execution_failed", processFailureMessage(result), { details: { process: result } }), { process: result })));
256
+ }, rejectPromise);
257
+ },
244
258
  });
245
259
 
246
260
  if (signal?.aborted) rejectCancelled();
247
261
  });
248
262
  }
249
263
  }
250
-
251
264
  function processResult(code, stdout, stderr, retainedStdout = null, retainedStderr = null) {
252
265
  const stderrBuffer = stderr instanceof BoundedOutput ? stderr : null;
253
266
  const result = {
@@ -265,7 +278,6 @@ function processResult(code, stdout, stderr, retainedStdout = null, retainedStde
265
278
  }
266
279
  return result;
267
280
  }
268
-
269
281
  export function boundedErrorMessage(error) {
270
282
  const message = error instanceof Error ? error.message : String(error);
271
283
  return message.replace(/[\r\n]+/g, " ").slice(0, 4096) || "tool call failed";
@@ -0,0 +1,50 @@
1
+ import {
2
+ effectiveForegroundTimeoutSeconds,
3
+ remoteForegroundDefaultSeconds,
4
+ remoteDurableProcessTimeoutSeconds,
5
+ } from "../shared/foreground-timeout.mjs";
6
+ import { MAX_PROCESS_TIMEOUT_SECONDS } from "./execution-limits.mjs";
7
+
8
+ // Relay-origin process one-shots should be routed through durable delivery before reaching this module.
9
+ // Keep a narrower defense-in-depth ceiling for any accidental direct foreground entry without advertising
10
+ // that value as a supported remote process delivery contract.
11
+ const REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS = 30;
12
+
13
+ export function processForegroundTimeoutSeconds(tool, value, context = {}) {
14
+ const remote = context.origin === "relay";
15
+ const timeout = effectiveForegroundTimeoutSeconds({
16
+ tool,
17
+ value,
18
+ remote,
19
+ localDefault: 120,
20
+ localMaximum: MAX_PROCESS_TIMEOUT_SECONDS,
21
+ });
22
+ if (remote && timeout > REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS) {
23
+ throw new RangeError(`foreground timeout must be an integer from 1 to ${REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS}`);
24
+ }
25
+ return timeout;
26
+ }
27
+
28
+ export function registeredCommandTimeoutSeconds(args, command, context = {}) {
29
+ const remote = context.origin === "relay";
30
+ const requested = args.timeout_seconds === undefined
31
+ ? (remote ? remoteForegroundDefaultSeconds("run_local_command") : command.timeoutSeconds)
32
+ : processForegroundTimeoutSeconds("run_local_command", args.timeout_seconds, context);
33
+ return Math.min(
34
+ requested,
35
+ command.timeoutSeconds,
36
+ remote ? REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS : MAX_PROCESS_TIMEOUT_SECONDS,
37
+ );
38
+ }
39
+
40
+ export function durableProcessExecutionTimeoutSeconds(value) {
41
+ return remoteDurableProcessTimeoutSeconds(value);
42
+ }
43
+
44
+ export function durableRegisteredCommandTimeoutSeconds(args, command) {
45
+ return Math.min(
46
+ remoteDurableProcessTimeoutSeconds(args.timeout_seconds),
47
+ command.timeoutSeconds,
48
+ MAX_PROCESS_TIMEOUT_SECONDS,
49
+ );
50
+ }
@@ -1,4 +1,4 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { execFile, spawnSync } from "node:child_process";
2
2
  import { performance } from "node:perf_hooks";
3
3
 
4
4
  const COMMAND_TIMEOUT_MS = 3000;
@@ -25,15 +25,30 @@ export function processStartTimeMs(pid) {
25
25
  const parsed = normalizePid(pid);
26
26
  if (!parsed) return null;
27
27
  if (parsed === process.pid) return currentProcessStartTimeMs();
28
- if (process.platform === "win32") {
29
- const command = `(Get-CimInstance Win32_Process -Filter "ProcessId = ${parsed}").CreationDate.ToUniversalTime().ToString('o')`;
30
- const result = runBounded("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command]);
31
- return result.ok ? parseTime(result.stdout) : null;
32
- }
33
- const result = runBounded("ps", ["-p", String(parsed), "-o", "lstart="]);
28
+ const [command, args] = processStartCommand(parsed);
29
+ const result = runBounded(command, args);
30
+ return result.ok ? parseTime(result.stdout) : null;
31
+ }
32
+
33
+ export async function processStartTimeMsAsync(pid) {
34
+ const parsed = normalizePid(pid);
35
+ if (!parsed) return null;
36
+ if (parsed === process.pid) return currentProcessStartTimeMs();
37
+ const [command, args] = processStartCommand(parsed);
38
+ const result = await runBoundedAsync(command, args);
34
39
  return result.ok ? parseTime(result.stdout) : null;
35
40
  }
36
41
 
42
+ export function processState(pid) {
43
+ const parsed = normalizePid(pid);
44
+ if (!parsed || process.platform === "win32") return "unknown";
45
+ const result = runBounded("ps", ["-p", String(parsed), "-o", "state="]);
46
+ if (!result.ok) return "unknown";
47
+ const state = result.stdout.trim().charAt(0).toUpperCase();
48
+ if (state === "Z") return "zombie";
49
+ return state ? "running" : "unknown";
50
+ }
51
+
37
52
  export function processCommandLine(pid) {
38
53
  const parsed = normalizePid(pid);
39
54
  if (!parsed) return "";
@@ -119,10 +134,19 @@ export function inspectProcessInstance(owner, options = {}) {
119
134
  };
120
135
  }
121
136
 
137
+ function processStartCommand(pid) {
138
+ if (process.platform === "win32") {
139
+ const command = `(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CreationDate.ToUniversalTime().ToString('o')`;
140
+ return ["powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command]];
141
+ }
142
+ return ["ps", ["-p", String(pid), "-o", "lstart="]];
143
+ }
144
+
122
145
  function runBounded(command, args) {
123
146
  const result = spawnSync(command, args, {
124
147
  encoding: "utf8",
125
148
  timeout: COMMAND_TIMEOUT_MS,
149
+ killSignal: "SIGKILL",
126
150
  maxBuffer: COMMAND_OUTPUT_BYTES,
127
151
  windowsHide: true,
128
152
  env: process.platform === "win32" ? process.env : { ...process.env, LC_ALL: "C", LANG: "C" },
@@ -131,6 +155,16 @@ function runBounded(command, args) {
131
155
  return { ok: !result.error && result.status === 0, stdout: String(result.stdout || "") };
132
156
  }
133
157
 
158
+ function runBoundedAsync(command, args) {
159
+ return new Promise((resolvePromise) => {
160
+ execFile(command, args, {
161
+ encoding: "utf8", timeout: COMMAND_TIMEOUT_MS, killSignal: "SIGKILL",
162
+ maxBuffer: COMMAND_OUTPUT_BYTES, windowsHide: true,
163
+ env: process.platform === "win32" ? process.env : { ...process.env, LC_ALL: "C", LANG: "C" },
164
+ }, (error, stdout) => resolvePromise({ ok: !error, stdout: String(stdout || "") }));
165
+ });
166
+ }
167
+
134
168
  function normalizePid(value) {
135
169
  const parsed = Number(value);
136
170
  return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
@@ -0,0 +1,50 @@
1
+ // @ts-check
2
+ import { BridgeError } from "./errors.mjs";
3
+ import { processOutcomeUnknownAfterSpawn } from "./process-result-projection.mjs";
4
+
5
+ /** @param {unknown} error @param {boolean} nonReplayable */
6
+ export function processPreSpawnFailure(error, nonReplayable) {
7
+ if (!nonReplayable || error instanceof BridgeError) return error;
8
+ return new BridgeError("execution_failed", "process failed before spawn", {
9
+ cause: error instanceof Error ? error : undefined,
10
+ retryable: false,
11
+ details: { reason: "process_failed_before_spawn" },
12
+ });
13
+ }
14
+
15
+ /** @param {boolean} nonReplayable @param {AbortSignal | undefined} signal */
16
+ export function processCancellationFailure(nonReplayable, signal) {
17
+ const reason = signal?.reason;
18
+ const code = reason instanceof BridgeError && reason.code === "timeout" ? "timeout" : "cancelled";
19
+ if (nonReplayable) return processOutcomeUnknownAfterSpawn(code);
20
+ return new BridgeError(code, reason instanceof Error ? reason.message : "tool call cancelled", {
21
+ retryable: false,
22
+ cause: reason instanceof Error ? reason : undefined,
23
+ details: { side_effects_started: true, termination_requested: true, effect_settlement: "pending" },
24
+ });
25
+ }
26
+
27
+ /** @param {boolean} nonReplayable @param {number} timeoutMs */
28
+ export function processTimeoutFailure(nonReplayable, timeoutMs) {
29
+ if (nonReplayable) return processOutcomeUnknownAfterSpawn("timeout");
30
+ return new BridgeError("timeout", `command timed out after ${timeoutMs}ms`, {
31
+ retryable: false,
32
+ details: { side_effects_started: true, termination_requested: true, effect_settlement: "pending" },
33
+ });
34
+ }
35
+
36
+ /** @param {boolean} nonReplayable @param {string} trigger @param {unknown} fallback @param {Record<string, unknown>} [details] */
37
+ export function processPostSpawnFailure(nonReplayable, trigger, fallback, details = {}) {
38
+ return nonReplayable ? processOutcomeUnknownAfterSpawn(trigger, details) : fallback;
39
+ }
40
+
41
+ /** @param {boolean} nonReplayable @param {unknown} error @param {boolean} started */
42
+ export function processChildErrorFailure(nonReplayable, error, started) {
43
+ if (!nonReplayable) return error;
44
+ if (started) return processOutcomeUnknownAfterSpawn("process_error");
45
+ return new BridgeError("execution_failed", "process failed before spawn", {
46
+ cause: error instanceof Error ? error : undefined,
47
+ retryable: false,
48
+ details: { reason: "process_failed_before_spawn" },
49
+ });
50
+ }
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import { MCP_TEXT_PROJECTION_KEY } from "../shared/result-projection.mjs";
4
+ import { BridgeError } from "./errors.mjs";
4
5
 
5
6
  /** @param {Record<string, any>} value */
6
7
  export function publicProcessToolResult(value) {
@@ -12,6 +13,27 @@ export function publicProcessToolResult(value) {
12
13
  return { ...value, [MCP_TEXT_PROJECTION_KEY]: summary };
13
14
  }
14
15
 
16
+ /** @param {string} trigger @param {Record<string, unknown>} [details] */
17
+ export function processOutcomeUnknownAfterSpawn(trigger, details = {}) {
18
+ const terminationRequested = trigger === "cancelled" || trigger === "timeout" || trigger === "resource_binding";
19
+ return new BridgeError(
20
+ "execution_failed",
21
+ "command execution may have partially completed; the outcome is unknown. Inspect command effects before retrying.",
22
+ {
23
+ expose: true,
24
+ retryable: false,
25
+ details: {
26
+ ...details,
27
+ reason: "process_outcome_unknown_after_spawn",
28
+ trigger: String(trigger || "unknown").slice(0, 64),
29
+ side_effects_started: "unknown",
30
+ termination_requested: terminationRequested,
31
+ effect_settlement: terminationRequested ? "pending" : "unknown",
32
+ },
33
+ },
34
+ );
35
+ }
36
+
15
37
  /** @param {Record<string, any>} result */
16
38
  export function processFailureMessage(result) {
17
39
  const source = String(result.stderr || result.stdout || `process exited ${result.code}`)
@@ -0,0 +1,47 @@
1
+ export function waitForSpawn(child) {
2
+ return new Promise((resolvePromise, rejectPromise) => {
3
+ const onSpawn = () => { cleanup(); resolvePromise(); };
4
+ const onError = (error) => { cleanup(); rejectPromise(error); };
5
+ const cleanup = () => {
6
+ child.off("spawn", onSpawn);
7
+ child.off("error", onError);
8
+ };
9
+ child.once("spawn", onSpawn);
10
+ child.once("error", onError);
11
+ });
12
+ }
13
+
14
+ export function sessionHasOutputAfter(session, stdoutOffset, stderrOffset) {
15
+ return session.stdout.totalBytes > stdoutOffset || session.stderr.totalBytes > stderrOffset;
16
+ }
17
+
18
+ export function notifySessionWaiters(session) {
19
+ for (const waiter of [...session.waiters]) waiter();
20
+ }
21
+
22
+ export function waitForSessionChange(session, waitMs, cancellationCheck, options = {}) {
23
+ return new Promise((resolvePromise, rejectPromise) => {
24
+ let timer;
25
+ const done = () => {
26
+ cleanup();
27
+ try {
28
+ cancellationCheck();
29
+ resolvePromise();
30
+ } catch (error) {
31
+ rejectPromise(error);
32
+ }
33
+ };
34
+ const cleanup = () => {
35
+ if (timer) clearTimeout(timer);
36
+ session.waiters.delete(done);
37
+ };
38
+ session.waiters.add(done);
39
+ timer = setTimeout(done, waitMs);
40
+ if (options.keepAlive !== true) timer.unref?.();
41
+ });
42
+ }
43
+
44
+ export function boundedErrorMessage(error) {
45
+ const message = error instanceof Error ? error.message : String(error);
46
+ return message.replace(/[\r\n]+/g, " ").slice(0, 4096) || "process failed";
47
+ }
@@ -0,0 +1,10 @@
1
+ export function beginRemoteProcessSessionActivity(context, guard) {
2
+ if (context?.origin !== "relay" || !guard) return false;
3
+ try { guard.beginActivity?.(); } catch { /* Auxiliary power management must not change session startup. */ }
4
+ return true;
5
+ }
6
+
7
+ export function endRemoteProcessSessionActivity(held, guard) {
8
+ if (!held || !guard) return;
9
+ try { guard.endActivity?.(); } catch { /* Auxiliary power management must not change session settlement. */ }
10
+ }
@@ -0,0 +1,73 @@
1
+ import { BridgeError } from "./errors.mjs";
2
+ import { createMonotonicDeadline } from "./monotonic-deadline.mjs";
3
+ import { notifySessionWaiters, waitForSessionChange } from "./process-session-events.mjs";
4
+
5
+ export async function terminateProcessSessions({
6
+ sessions,
7
+ matches = () => true,
8
+ terminateTree,
9
+ signal = "SIGKILL",
10
+ waitMs = 5_000,
11
+ mode = "shutdown",
12
+ }) {
13
+ const contract = terminationContract(mode);
14
+ let settledCount = 0;
15
+ let failure = null;
16
+ const settlements = [];
17
+ for (const [id, session] of [...sessions.entries()]) {
18
+ if (!matches(session)) continue;
19
+ notifySessionWaiters(session);
20
+ if (session.closedAt !== null) {
21
+ if (sessions.get(id) === session) sessions.delete(id);
22
+ settledCount += 1;
23
+ continue;
24
+ }
25
+ if (!session.child) {
26
+ failure ||= new Error(contract.missingChild);
27
+ continue;
28
+ }
29
+ let requested = false;
30
+ try { requested = terminateTree(session.child, signal) === true; }
31
+ catch { /* The retained session handle makes failed termination delivery retryable. */ }
32
+ if (!requested) {
33
+ failure ||= new Error(contract.requestFailure);
34
+ continue;
35
+ }
36
+ settlements.push(settleProcessSession(sessions, id, session, waitMs).then((settled) => {
37
+ if (settled) settledCount += 1;
38
+ else failure ||= new Error(contract.settlementFailure);
39
+ }));
40
+ }
41
+ await Promise.all(settlements);
42
+ if (failure) {
43
+ throw new BridgeError("unavailable", contract.unavailable, { cause: failure, retryable: true });
44
+ }
45
+ return settledCount;
46
+ }
47
+
48
+ async function settleProcessSession(sessions, id, session, waitMs) {
49
+ const deadline = createMonotonicDeadline(waitMs);
50
+ while (session.closedAt === null && !deadline.expired()) {
51
+ await waitForSessionChange(session, Math.max(1, deadline.remainingMs()), () => {}, { keepAlive: true });
52
+ }
53
+ if (session.closedAt === null) return false;
54
+ if (sessions.get(id) === session) sessions.delete(id);
55
+ return true;
56
+ }
57
+
58
+ function terminationContract(mode) {
59
+ if (mode === "authority_revocation") {
60
+ return {
61
+ missingChild: "process-session termination request could not be delivered",
62
+ requestFailure: "process-session termination request could not be delivered",
63
+ settlementFailure: "process-session termination did not settle before the revocation deadline",
64
+ unavailable: "process-session authority revocation was incomplete; retained revocation must be retried",
65
+ };
66
+ }
67
+ return {
68
+ missingChild: "process-session shutdown has no retained child handle",
69
+ requestFailure: "process-session shutdown request could not be delivered",
70
+ settlementFailure: "process-session shutdown did not settle before the runtime teardown deadline",
71
+ unavailable: "process-session shutdown was incomplete; runtime ownership must be retained",
72
+ };
73
+ }