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
@@ -1,29 +1,38 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { randomBytes } from "node:crypto";
3
2
  import { LocalRuntime } from "./runtime.mjs";
3
+ import { packageVersion } from "./package-identity.mjs";
4
+ import { validateToolArguments } from "./tool-executor.mjs";
4
5
  import { classifyOperationalError, createLogger } from "./log.mjs";
5
6
  import { publicError } from "./errors.mjs";
6
7
  import {
7
8
  MCP_INSTRUCTIONS,
8
- MCP_PROTOCOL_VERSION,
9
- MCP_SUPPORTED_PROTOCOL_VERSIONS,
10
9
  SERVER_NAME,
11
10
  rpcError,
12
11
  rpcResult,
13
12
  toolResult,
14
13
  toolsForPolicy,
15
14
  } from "./tools.mjs";
15
+ import {
16
+ MCP_PROTOCOL_VERSIONS, MCP_REMOVED_PROTOCOL_MESSAGE, McpProtocolError, cacheableResult,
17
+ discoverResult, emptySubscriptionAcknowledgement, serverImplementation, subscriptionCompleteResult,
18
+ validateRequestMetadata, validateSubscriptionRequest,
19
+ } from "../shared/mcp-protocol.mjs";
16
20
 
17
21
  const MAX_LINE_BYTES = 8 * 1024 * 1024;
18
- const PACKAGE_VERSION = String(JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version);
22
+ const MAX_PENDING_TOOL_CALLS = 32;
23
+ const MCP_SERVER_INFO = Object.freeze(serverImplementation({
24
+ name: SERVER_NAME, title: "Machine Bridge MCP", version: packageVersion,
25
+ description: "Workspace-scoped local coding tools over MCP stdio or authenticated remote relay.",
26
+ }));
27
+ const MCP_SERVER_CAPABILITIES = Object.freeze({ tools: Object.freeze({ listChanged: false }) });
28
+ const MCP_DISCOVERY_TTL_MS = 300_000;
29
+ const MCP_TOOL_LIST_TTL_MS = 300_000;
30
+ const MCP_LIMIT_EXCEEDED = -31900;
19
31
 
20
32
  export async function runStdioServer({ workspace, policy, logLevel = "info", jobRoot = "", resources = {}, resourceStatePath = "", browserStateRoot = "" }) {
21
33
  const logger = createLogger({ component: "stdio", level: logLevel, stderrOnly: true, color: false });
22
34
  const runtime = new LocalRuntime({ workspace, policy, logger, jobRoot, resources, resourceStatePath, browserStateRoot });
23
35
  const pending = new Map();
24
- let negotiatedVersion = MCP_PROTOCOL_VERSION;
25
- let initialized = false;
26
-
27
36
  const writes = new Set();
28
37
  const send = (message) => {
29
38
  if (message === null || message === undefined) return;
@@ -47,84 +56,124 @@ export async function runStdioServer({ workspace, policy, logLevel = "info", job
47
56
  });
48
57
  for (const callId of pending.values()) runtime.cancelCall(callId, "stdio input closed");
49
58
  await Promise.allSettled([...writes]);
50
- runtime.stop();
59
+ await runtime.stop();
51
60
 
52
61
  async function handleLine(line) {
53
62
  const message = parseJsonRpcLine(line, send);
54
- if (!message || typeof message.method !== "string") return;
55
- if (await handleControlMessage(message)) return;
56
- if (!initialized) {
57
- send(rpcError(message.id, -32002, "Server is not initialized"));
63
+ if (!message) return;
64
+ if (typeof message.method !== "string") {
65
+ send(rpcError(message.id, -32600, "Clients must not send JSON-RPC responses"));
58
66
  return;
59
67
  }
60
- await handleInitializedMessage(message);
61
- }
62
-
63
- async function handleControlMessage(message) {
64
- if (message.method === "initialize") {
65
- const requested = asObject(message.params).protocolVersion;
66
- negotiatedVersion = typeof requested === "string" && MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(requested)
67
- ? requested
68
- : MCP_PROTOCOL_VERSION;
69
- initialized = true;
70
- send(rpcResult(message.id, await initializationResult(negotiatedVersion, runtime)));
71
- return true;
72
- }
73
- if (message.method === "notifications/initialized") return true;
74
68
  if (message.method === "notifications/cancelled") {
75
- const requestId = asObject(message.params).requestId;
76
- const callId = pending.get(jsonRpcIdKey(requestId));
77
- if (callId) runtime.cancelCall(callId, "MCP cancellation notification");
78
- return true;
69
+ cancelRequest(message);
70
+ return;
79
71
  }
80
- if (message.method === "logging/setLevel" || message.method === "ping") {
81
- send(rpcResult(message.id, {}));
82
- return true;
72
+ if (message.method === "initialize") {
73
+ send(rpcError(message.id, -32601, MCP_REMOVED_PROTOCOL_MESSAGE, { supported: [...MCP_PROTOCOL_VERSIONS] }));
74
+ return;
83
75
  }
84
- return false;
76
+ try {
77
+ validateRequestMetadata(message, MCP_PROTOCOL_VERSIONS);
78
+ } catch (error) {
79
+ if (error instanceof McpProtocolError) {
80
+ send(rpcError(message.id, error.code, error.message, error.data));
81
+ return;
82
+ }
83
+ throw error;
84
+ }
85
+ await handleMessage(message);
85
86
  }
86
87
 
87
- async function handleInitializedMessage(message) {
88
+ function cancelRequest(message) {
89
+ const requestId = asObject(message.params).requestId;
90
+ const callId = pending.get(jsonRpcIdKey(requestId));
91
+ if (callId) runtime.cancelCall(callId, "MCP cancellation notification");
92
+ }
93
+
94
+ async function handleMessage(message) {
95
+ if (!("id" in message) || message.id === null) {
96
+ if (!message.method.startsWith("notifications/")) {
97
+ send(rpcError(null, -32600, "MCP requests require a non-null request id"));
98
+ }
99
+ return;
100
+ }
101
+ if (message.method === "server/discover") {
102
+ send(rpcResult(message.id, discoverResult({
103
+ supportedVersions: MCP_PROTOCOL_VERSIONS,
104
+ capabilities: MCP_SERVER_CAPABILITIES,
105
+ instructions: MCP_INSTRUCTIONS,
106
+ ttlMs: MCP_DISCOVERY_TTL_MS,
107
+ serverInfo: MCP_SERVER_INFO,
108
+ })));
109
+ return;
110
+ }
88
111
  if (message.method === "tools/list") {
89
- send(rpcResult(message.id, { tools: toolsForPolicy(policy) }));
112
+ send(rpcResult(message.id, cacheableResult(
113
+ { tools: toolsForPolicy(policy) },
114
+ { ttlMs: MCP_TOOL_LIST_TTL_MS, cacheScope: "private", serverInfo: MCP_SERVER_INFO },
115
+ )));
116
+ return;
117
+ }
118
+ if (message.method === "subscriptions/listen") {
119
+ try {
120
+ validateSubscriptionRequest(message);
121
+ send(emptySubscriptionAcknowledgement(message.id));
122
+ send(rpcResult(message.id, subscriptionCompleteResult(message.id, MCP_SERVER_INFO)));
123
+ } catch (error) {
124
+ if (error instanceof McpProtocolError) send(rpcError(message.id, error.code, error.message, error.data));
125
+ else throw error;
126
+ }
90
127
  return;
91
128
  }
92
129
  if (message.method === "tools/call") {
93
130
  await handleToolCall(message);
94
131
  return;
95
132
  }
96
- send(rpcError(message.id, -32601, `Method not found: ${message.method}`));
133
+ send(rpcError(message.id, -32601, "Method not found"));
97
134
  }
98
135
 
99
136
  async function handleToolCall(message) {
100
- if (!("id" in message) || message.id === null) {
101
- send(rpcError(null, -32600, "tools/call requires a non-null request id"));
102
- return;
103
- }
104
137
  const params = asObject(message.params);
105
138
  const name = typeof params.name === "string" ? params.name : "";
106
139
  if (!name) {
107
140
  send(rpcError(message.id, -32602, "tools/call requires a tool name"));
108
141
  return;
109
142
  }
110
- const args = asObject(params.arguments);
111
- const callId = `stdio_${randomBytes(16).toString("hex")}`;
143
+ if (!toolsForPolicy(policy).some((tool) => tool.name === name)) {
144
+ send(rpcError(message.id, -32602, "Unknown or unavailable tool"));
145
+ return;
146
+ }
147
+ const rawArgs = params.arguments === undefined ? {} : params.arguments;
148
+ const validation = validateToolArguments(name, rawArgs);
149
+ if (!validation.known || !validation.valid) {
150
+ send(rpcError(message.id, -32602, !validation.known
151
+ ? "Unknown tool"
152
+ : "Tool arguments do not match the input schema", validation.valid ? undefined : {
153
+ validation_issues: validation.issues,
154
+ }));
155
+ return;
156
+ }
157
+ if (pending.size >= MAX_PENDING_TOOL_CALLS) {
158
+ send(rpcError(message.id, MCP_LIMIT_EXCEEDED, "Too many concurrent tool calls"));
159
+ return;
160
+ }
112
161
  const key = jsonRpcIdKey(message.id);
113
162
  if (key && pending.has(key)) {
114
163
  send(rpcError(message.id, -32600, "Duplicate in-flight JSON-RPC request id"));
115
164
  return;
116
165
  }
166
+ const callId = `stdio_${randomBytes(16).toString("hex")}`;
117
167
  if (key) pending.set(key, callId);
118
168
  try {
119
- const result = await runtime.executeTool(name, args, { callId, origin: "stdio" });
120
- send(rpcResult(message.id, toolResult(result)));
169
+ const result = await runtime.executeTool(name, rawArgs, { callId, origin: "stdio" });
170
+ send(rpcResult(message.id, toolResult(result, false, MCP_SERVER_INFO)));
121
171
  } catch (error) {
122
- send(rpcResult(message.id, toolResult({ error: publicError(error) }, true)));
172
+ send(rpcResult(message.id, toolResult({ error: publicError(error) }, true, MCP_SERVER_INFO)));
123
173
  } finally {
124
174
  if (key) pending.delete(key);
125
175
  }
126
176
  }
127
-
128
177
  }
129
178
 
130
179
  function parseJsonRpcLine(line, send) {
@@ -140,25 +189,6 @@ function parseJsonRpcLine(line, send) {
140
189
  return message;
141
190
  }
142
191
 
143
- async function initializationResult(protocolVersion, runtime) {
144
- const bootstrap = await runtime.sessionBootstrap({ path: "." }).catch((error) => {
145
- runtime.logger?.debug?.("session bootstrap unavailable during initialize", { error_class: classifyOperationalError(error) });
146
- return null;
147
- });
148
- const localInstructions = bootstrap?.instructions ? `\n\n--- LOCAL SESSION INSTRUCTIONS ---\n${bootstrap.instructions}` : "";
149
- return {
150
- protocolVersion,
151
- capabilities: { tools: { listChanged: false }, logging: {} },
152
- serverInfo: {
153
- name: SERVER_NAME,
154
- title: "Machine Bridge MCP",
155
- version: PACKAGE_VERSION,
156
- description: "Workspace-scoped local coding tools over MCP stdio or authenticated remote relay.",
157
- },
158
- instructions: `${MCP_INSTRUCTIONS}${localInstructions}`,
159
- };
160
- }
161
-
162
192
  function consumeBoundedJsonLines(stream, { maxLineBytes, onLine, onOversize }) {
163
193
  return new Promise((resolvePromise, rejectPromise) => {
164
194
  let chunks = [];
@@ -0,0 +1,30 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {{provider?: unknown}} DeviceIdentitySummaryInput */
4
+ /** @typedef {{url?: unknown, mcpServerUrl?: unknown, name?: unknown, deviceIdentity?: DeviceIdentitySummaryInput, pendingDeviceIdentity?: unknown, previousDeviceIdentities?: unknown[], oauthTokenVersion?: unknown}} WorkerSummaryInput */
5
+
6
+ /** @param {{schemaVersion?: unknown, policy?: Record<string, unknown>, worker?: WorkerSummaryInput, resources?: Record<string, unknown>}} state */
7
+ export function supportStateProjection(state) {
8
+ const worker = state?.worker && typeof state.worker === "object" ? state.worker : {};
9
+ const currentIdentity = worker.deviceIdentity && typeof worker.deviceIdentity === "object" ? worker.deviceIdentity : null;
10
+ const previous = Array.isArray(worker.previousDeviceIdentities) ? worker.previousDeviceIdentities : [];
11
+ const resources = state?.resources && typeof state.resources === "object" && !Array.isArray(state.resources) ? state.resources : {};
12
+ const policy = state?.policy && typeof state.policy === "object" ? state.policy : {};
13
+ return {
14
+ schema_version: Number.isSafeInteger(state?.schemaVersion) ? state.schemaVersion : null,
15
+ policy: {
16
+ profile: typeof policy.profile === "string" ? policy.profile : null,
17
+ origin: typeof policy.origin === "string" ? policy.origin : null,
18
+ revision: Number.isSafeInteger(policy.revision) ? policy.revision : null,
19
+ },
20
+ worker: {
21
+ configured: Boolean(worker.url || worker.mcpServerUrl || worker.name || currentIdentity),
22
+ endpoint_configured: Boolean(worker.url || worker.mcpServerUrl),
23
+ oauth_token_version_configured: Boolean(worker.oauthTokenVersion),
24
+ device_root_provider: currentIdentity ? String(currentIdentity.provider || "portable-jwk-v1") : null,
25
+ pending_device_root: Boolean(worker.pendingDeviceIdentity),
26
+ retired_device_root_count: previous.length,
27
+ },
28
+ resource_count: Object.keys(resources).length,
29
+ };
30
+ }
@@ -0,0 +1,76 @@
1
+ // @ts-check
2
+
3
+ const TUNNEL_INTERFACE = /^(?:utun|tun|tap|ppp|ipsec|wg|gif|stf)/i;
4
+ const LOOPBACK_INTERFACE = /^lo\d*$/i;
5
+ const PHYSICAL_INTERFACE = /^(?:en|eth|wlan|wl|wifi|wi-fi)\d*$/i;
6
+
7
+ /** @typedef {{code?: number, stdout?: string}} FixedProcessResult */
8
+ /** @typedef {(command: string, args: string[], timeoutMs: number, capture: boolean, maxOutputBytes: number, context: unknown) => Promise<FixedProcessResult>} FixedInternalRunner */
9
+ /**
10
+ * @typedef {{
11
+ * platform?: string,
12
+ * runFixedInternal?: FixedInternalRunner,
13
+ * classifyError?: (error: unknown) => string,
14
+ * context?: unknown,
15
+ * }} SystemNetworkRouteOptions
16
+ */
17
+
18
+ /** @param {SystemNetworkRouteOptions} [options] */
19
+ export async function systemNetworkRouteCheck({
20
+ platform = process.platform,
21
+ runFixedInternal,
22
+ classifyError = () => "unavailable",
23
+ context = {},
24
+ } = {}) {
25
+ try {
26
+ const route = await inspectSystemNetworkRoute({ platform, runFixedInternal, context });
27
+ return route.supported && route.available ? {
28
+ layer: "system-network-route",
29
+ ok: true,
30
+ route_class: route.route_class,
31
+ operating_system_interception: route.operating_system_interception === true,
32
+ } : {
33
+ layer: "system-network-route",
34
+ ok: false,
35
+ skipped: true,
36
+ error_class: route.supported ? "unavailable" : "unsupported_platform",
37
+ };
38
+ } catch (error) {
39
+ return {
40
+ layer: "system-network-route",
41
+ ok: false,
42
+ skipped: true,
43
+ error_class: classifyError(error),
44
+ };
45
+ }
46
+ }
47
+
48
+ /** @param {SystemNetworkRouteOptions} [options] */
49
+ export async function inspectSystemNetworkRoute({
50
+ platform = process.platform,
51
+ runFixedInternal,
52
+ context = {},
53
+ } = {}) {
54
+ if (platform !== "darwin" || typeof runFixedInternal !== "function") return { supported: false };
55
+ const result = await runFixedInternal("/sbin/route", ["-n", "get", "default"], 5_000, true, 16 * 1024, context);
56
+ if (result?.code !== 0) return { supported: true, available: false };
57
+ const match = String(result.stdout || "").match(/^\s*interface:\s*(\S+)\s*$/mi);
58
+ if (!match) return { supported: true, available: false };
59
+ const routeClass = classifySystemRouteInterface(match[1]);
60
+ return {
61
+ supported: true,
62
+ available: true,
63
+ route_class: routeClass,
64
+ operating_system_interception: routeClass === "tunnel-or-vpn",
65
+ };
66
+ }
67
+
68
+ /** @param {unknown} value */
69
+ export function classifySystemRouteInterface(value) {
70
+ const name = String(value || "").trim();
71
+ if (!name) return "unknown";
72
+ if (TUNNEL_INTERFACE.test(name)) return "tunnel-or-vpn";
73
+ if (LOOPBACK_INTERFACE.test(name)) return "loopback";
74
+ if (PHYSICAL_INTERFACE.test(name)) return "physical-or-other";
75
+ return "other";
76
+ }
@@ -0,0 +1,20 @@
1
+ export function systemdRemovalDecision({ definitionPresent, disableCode, status }) {
2
+ const state = String(status?.state || "unknown");
3
+ const active = status?.active;
4
+ if (active === true || ["activating", "deactivating", "reloading", "maintenance"].includes(state)) {
5
+ return { removable: false, alreadyAbsent: false, reason: "service_active_or_transitioning" };
6
+ }
7
+ const safelyInactive = active === false && ["inactive", "failed"].includes(state);
8
+ const safelyAbsent = definitionPresent !== true && active === false && state === "unknown";
9
+ if (!safelyInactive && !safelyAbsent) {
10
+ return { removable: false, alreadyAbsent: false, reason: "status_unavailable" };
11
+ }
12
+ if (definitionPresent === true && Number(disableCode) !== 0) {
13
+ return { removable: false, alreadyAbsent: false, reason: "disable_failed" };
14
+ }
15
+ return {
16
+ removable: true,
17
+ alreadyAbsent: definitionPresent !== true,
18
+ reason: definitionPresent === true ? "disabled" : "already_absent",
19
+ };
20
+ }
@@ -1,5 +1,18 @@
1
1
  import { performance } from "node:perf_hooks";
2
- import { errorCode, normalizeBridgeError } from "./errors.mjs";
2
+ import catalog from "../shared/tool-catalog.json" with { type: "json" };
3
+ import { compileToolArgumentValidators } from "../shared/tool-argument-validation.mjs";
4
+ import { isRemoteDurableProcessTool, remoteDurableProcessTimeoutSeconds } from "../shared/foreground-timeout.mjs";
5
+ import { BridgeError, errorCode, normalizeBridgeError } from "./errors.mjs";
6
+ import { resourceAdmissionLogFields } from "./resource-admission-diagnostics.mjs";
7
+ import { normalizeToolResult } from "./tool-result-boundary.mjs";
8
+ import { createSecurityAuditFailureReporter } from "./security-audit-warning.mjs";
9
+ import { enqueueSecurityAudit } from "./security-audit-dispatch.mjs";
10
+
11
+ const TOOL_ARGUMENTS = compileToolArgumentValidators(catalog);
12
+
13
+ export function validateToolArguments(tool, value) {
14
+ return TOOL_ARGUMENTS.validate(tool, value);
15
+ }
3
16
 
4
17
  export class ToolExecutor {
5
18
  constructor(options = {}) {
@@ -9,18 +22,22 @@ export class ToolExecutor {
9
22
  this.accountAccessGate = options.accountAccessGate;
10
23
  this.operationAuthorizer = options.operationAuthorizer;
11
24
  this.observability = options.observability;
25
+ this.securityAudit = options.securityAudit || null;
12
26
  this.logger = options.logger || console;
27
+ this.onAuthorizedRelayActivityStart = typeof options.onAuthorizedRelayActivityStart === "function" ? options.onAuthorizedRelayActivityStart : null;
28
+ this.onAuthorizedRelayActivityEnd = typeof options.onAuthorizedRelayActivityEnd === "function" ? options.onAuthorizedRelayActivityEnd : null;
13
29
  this.safeMessage = typeof options.safeMessage === "function" ? options.safeMessage : (error) => String(error?.message || error || "operation failed");
14
30
  this.slowMs = Number.isFinite(Number(options.slowMs)) ? Math.max(1, Number(options.slowMs)) : 30_000;
15
31
  this.pipeline = composeMiddleware([
16
32
  lifecycleMiddleware(this.callRegistry),
17
- observabilityMiddleware(this.observability, this.logger, this.safeMessage, this.slowMs),
18
- authorizeMiddleware(this.policyGate, this.accountAccessGate, this.operationAuthorizer),
19
- ], invokeHandler(this.handlers));
33
+ observabilityMiddleware(this.observability, this.securityAudit, this.logger, this.safeMessage, this.slowMs),
34
+ authorizeMiddleware(this.policyGate, this.accountAccessGate, this.operationAuthorizer, this.callRegistry),
35
+ validateArgumentsMiddleware(),
36
+ ], invokeHandler(this.handlers, this.onAuthorizedRelayActivityStart, this.onAuthorizedRelayActivityEnd));
20
37
  }
21
38
 
22
39
  execute(tool, args = {}, request = {}) {
23
- return this.pipeline({ tool: String(tool || ""), args: isRecord(args) ? args : {}, request });
40
+ return this.pipeline({ tool: String(tool || ""), args, request });
24
41
  }
25
42
  }
26
43
 
@@ -28,19 +45,52 @@ export function composeMiddleware(middleware, terminal) {
28
45
  return middleware.reduceRight((next, current) => (operation) => current(operation, next), terminal);
29
46
  }
30
47
 
31
- function authorizeMiddleware(policyGate, accountAccessGate, operationAuthorizer) {
48
+ function authorizeMiddleware(policyGate, accountAccessGate, operationAuthorizer, callRegistry) {
32
49
  return async (operation, next) => {
33
50
  policyGate.assert(operation.tool);
34
51
  if (operation.context.origin === "relay") {
35
- const role = operation.request.authorization?.role;
52
+ const authorization = operation.request.authorization;
53
+ const role = authorization?.role;
36
54
  if (!role) throw new Error("relay tool call is missing an account role");
37
55
  accountAccessGate.assert(role, operation.tool);
38
- await operationAuthorizer?.authorize(operation);
56
+ operation.context.authority = accountAccessGate.authority(authorization, policyGate.policy, "relay");
57
+ callRegistry.bindPrincipal(operation.context.callId, operation.context.authority.principal);
58
+ const decision = await operationAuthorizer?.authorize(operation);
59
+ if (decision) operation.context.operationAuthorization = decision;
60
+ } else {
61
+ operation.context.authority = accountAccessGate.authority({}, policyGate.policy, "local");
62
+ callRegistry.bindPrincipal(operation.context.callId, operation.context.authority.principal);
63
+ }
64
+ return next(operation);
65
+ };
66
+ }
67
+
68
+ function validateArgumentsMiddleware() {
69
+ return async (operation, next) => {
70
+ const result = TOOL_ARGUMENTS.validate(operation.tool, operation.args);
71
+ if (!result.known) throw new BridgeError("not_found", `unknown tool: ${operation.tool}`);
72
+ if (!result.valid && !validRelayDurableProcessSchemaExtension(operation, result.issues)) {
73
+ throw new BridgeError("invalid_request", `tool arguments do not match the input schema: ${operation.tool}`, {
74
+ details: { tool: operation.tool, validation_issues: result.issues },
75
+ });
39
76
  }
40
77
  return next(operation);
41
78
  };
42
79
  }
43
80
 
81
+ function validRelayDurableProcessSchemaExtension(operation, issues) {
82
+ if (operation.context.origin !== "relay" || !isRemoteDurableProcessTool(operation.tool) || !Array.isArray(issues) || !issues.length) {
83
+ return false;
84
+ }
85
+ if (issues.some((issue) => issue?.instancePath !== "/timeout_seconds" || issue?.keyword !== "maximum")) return false;
86
+ try {
87
+ remoteDurableProcessTimeoutSeconds(operation.args?.timeout_seconds);
88
+ return true;
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
44
94
  function lifecycleMiddleware(callRegistry) {
45
95
  return async (operation, next) => {
46
96
  const context = callRegistry.open({
@@ -52,16 +102,18 @@ function lifecycleMiddleware(callRegistry) {
52
102
  operation.context = { ...operation.request.context, ...context };
53
103
  try {
54
104
  callRegistry.throwIfCancelled(operation.context);
55
- const result = await next(operation);
56
- callRegistry.throwIfCancelled(operation.context);
57
- return result;
105
+ // Handler return is the local settlement point. Cooperative handlers observe cancellation
106
+ // before or during cancellable work; a later signal must not retroactively relabel a
107
+ // completed side effect as cancelled after observability/audit already recorded completion.
108
+ return await next(operation);
58
109
  } finally {
59
110
  callRegistry.finish(context.callId);
60
111
  }
61
112
  };
62
113
  }
63
114
 
64
- function observabilityMiddleware(observability, logger, safeMessage, slowMs) {
115
+ function observabilityMiddleware(observability, securityAudit, logger, safeMessage, slowMs) {
116
+ const auditFailureReporter = createSecurityAuditFailureReporter(logger);
65
117
  return async (operation, next) => {
66
118
  const started = performance.now();
67
119
  observability.start(operation.tool);
@@ -71,41 +123,54 @@ function observabilityMiddleware(observability, logger, safeMessage, slowMs) {
71
123
  origin: operation.context.origin,
72
124
  }, "Tool call started");
73
125
  try {
74
- const result = await next(operation);
126
+ const normalizedResult = normalizeToolResult(await next(operation));
127
+ const result = normalizedResult.value;
75
128
  const durationMs = performance.now() - started;
76
129
  const slow = durationMs >= slowMs;
77
130
  observability.finish(operation.tool, { status: "completed", durationMs, slow });
131
+ enqueueSecurityAudit(securityAudit, operation, { outcome: "completed", durationMs, outputBytes: normalizedResult.bytes }, auditFailureReporter);
78
132
  logger.event?.("debug", slow ? "tool.call.slow" : "tool.call.completed", {
79
133
  call_id: shortCallId(operation.context.callId), tool: operation.tool, origin: operation.context.origin, duration_ms: durationMs,
134
+ authority_role: operation.context.authority?.principal?.role || "local",
135
+ risk_category: operation.context.operationAuthorization?.category || "ordinary",
80
136
  }, slow ? "Tool call completed slowly" : "Tool call completed");
81
137
  return result;
82
138
  } catch (error) {
83
- const normalized = normalizeBridgeError(error, { safeMessage: () => safeMessage(error, operation.args) });
139
+ const normalized = normalizeBridgeError(error, { safeMessage: () => safeMessage(error, operation.args, operation.context) });
84
140
  const durationMs = performance.now() - started;
85
141
  const code = errorCode(normalized);
86
142
  const status = code === "cancelled" ? "cancelled" : code === "timeout" ? "timeout" : "failed";
87
143
  observability.finish(operation.tool, { status, durationMs, errorCode: code, slow: durationMs >= slowMs });
144
+ enqueueSecurityAudit(securityAudit, operation, { outcome: status, durationMs, errorCode: code }, auditFailureReporter);
145
+ const resourceAdmission = resourceAdmissionLogFields(normalized);
88
146
  logger.event?.("debug", "tool.call.failed", {
89
147
  call_id: shortCallId(operation.context.callId), tool: operation.tool, origin: operation.context.origin,
90
148
  duration_ms: durationMs, error_code: code, retryable: normalized.retryable,
149
+ authority_role: operation.context.authority?.principal?.role || "local",
150
+ risk_category: operation.context.operationAuthorization?.category || "ordinary",
151
+ ...resourceAdmission,
91
152
  }, "Tool call failed");
92
153
  throw normalized;
93
154
  }
94
155
  };
95
156
  }
96
157
 
97
- function invokeHandler(handlers) {
158
+ function invokeHandler(handlers, onAuthorizedRelayActivityStart, onAuthorizedRelayActivityEnd) {
98
159
  return async (operation) => {
99
160
  const handler = handlers[operation.tool];
100
161
  if (typeof handler !== "function") throw new Error(`runtime handler is missing for tool: ${operation.tool}`);
101
- return handler(operation.args, operation.context);
162
+ const relayActivity = operation.context.origin === "relay";
163
+ if (relayActivity) bestEffortActivityHook(onAuthorizedRelayActivityStart);
164
+ try { return await handler(operation.args, operation.context); }
165
+ finally { if (relayActivity) bestEffortActivityHook(onAuthorizedRelayActivityEnd); }
102
166
  };
103
167
  }
104
168
 
105
- function shortCallId(value) {
106
- return String(value || "").slice(0, 20);
169
+ function bestEffortActivityHook(callback) {
170
+ try { callback?.(); } catch { /* Auxiliary power-management hooks must not change tool settlement. */ }
107
171
  }
108
172
 
109
- function isRecord(value) {
110
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
173
+
174
+ function shortCallId(value) {
175
+ return String(value || "").slice(0, 20);
111
176
  }
@@ -0,0 +1,51 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { BridgeError } from "./errors.mjs";
3
+
4
+ export const MAX_TOOL_RESULT_BYTES = 7 * 1024 * 1024;
5
+
6
+ export function normalizeToolResult(value, options = {}) {
7
+ const maximumBytes = positiveInteger(options.maximumBytes, MAX_TOOL_RESULT_BYTES);
8
+ let serialized;
9
+ try {
10
+ serialized = JSON.stringify(value, rejectUnsupportedJsonValue);
11
+ } catch (error) {
12
+ if (error instanceof BridgeError) throw error;
13
+ throw new BridgeError("internal_error", "tool returned a result that could not be serialized", {
14
+ cause: error instanceof Error ? error : undefined,
15
+ expose: false,
16
+ });
17
+ }
18
+ if (typeof serialized !== "string") {
19
+ throw new BridgeError("internal_error", "tool returned an unsupported result", { expose: false });
20
+ }
21
+ const bytes = Buffer.byteLength(serialized);
22
+ if (bytes > maximumBytes) {
23
+ throw new BridgeError("limit_exceeded", "tool result exceeded the relay response limit", {
24
+ retryable: false,
25
+ details: { maximum_bytes: maximumBytes, actual_bytes: bytes },
26
+ });
27
+ }
28
+ try {
29
+ return Object.freeze({ value: JSON.parse(serialized), bytes });
30
+ } catch (error) {
31
+ throw new BridgeError("internal_error", "tool result normalization failed", {
32
+ cause: error instanceof Error ? error : undefined,
33
+ expose: false,
34
+ });
35
+ }
36
+ }
37
+
38
+ function rejectUnsupportedJsonValue(_key, current) {
39
+ if (typeof current === "bigint" || typeof current === "function" || typeof current === "symbol" || typeof current === "undefined") {
40
+ throw new BridgeError("internal_error", "tool returned a non-JSON value", { expose: false });
41
+ }
42
+ if (typeof current === "number" && !Number.isFinite(current)) {
43
+ throw new BridgeError("internal_error", "tool returned a non-finite number", { expose: false });
44
+ }
45
+ return current;
46
+ }
47
+
48
+ function positiveInteger(value, fallback) {
49
+ const parsed = Number(value);
50
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
51
+ }
@@ -0,0 +1,41 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstatSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { filesystemIdentity, sameFilesystemIdentity } from "./filesystem-identity.mjs";
5
+ import { withOwnerStateLock } from "./owner-state-lock.mjs";
6
+ import { assertStateMaintenanceAvailable, expandHome, machineServiceControlRoot } from "./state.mjs";
7
+
8
+ const LOCK_PURPOSE = "toolchain-operation";
9
+
10
+ export function withToolchainOperationLock(stateRoot, callback, options = {}) {
11
+ if (typeof callback !== "function") throw new TypeError("toolchain operation lock requires a callback");
12
+ const root = resolve(expandHome(stateRoot));
13
+ assertStateMaintenanceAvailable(root);
14
+ const generation = stateGeneration(root);
15
+ const key = process.platform === "win32" ? root.toLowerCase() : root;
16
+ const digest = createHash("sha256").update(key).digest("hex").slice(0, 16);
17
+ return withOwnerStateLock(machineServiceControlRoot(options), async () => {
18
+ assertStateMaintenanceAvailable(root);
19
+ const current = stateGeneration(root);
20
+ if (!sameFilesystemIdentity(generation, current)) {
21
+ throw new Error("state root changed while waiting for private-toolchain ownership; retry from current state");
22
+ }
23
+ return callback();
24
+ }, {
25
+ purpose: LOCK_PURPOSE,
26
+ fileName: `toolchain-operation-${digest}.lock`,
27
+ label: "private toolchain operation",
28
+ timeoutMs: options.timeoutMs ?? 30_000,
29
+ });
30
+ }
31
+
32
+ function stateGeneration(root) {
33
+ let info;
34
+ try { info = lstatSync(root, { bigint: true }); }
35
+ catch (error) {
36
+ if (error?.code === "ENOENT") throw new Error("state root must exist before a persistent private-toolchain operation", { cause: error });
37
+ throw error;
38
+ }
39
+ if (info.isSymbolicLink() || !info.isDirectory()) throw new Error("state root must be a real directory before a persistent private-toolchain operation");
40
+ return filesystemIdentity(info, "private-toolchain state root");
41
+ }