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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. package/CHANGELOG.md +1142 -0
  2. package/CONTRIBUTING.md +23 -25
  3. package/GOVERNANCE.md +2 -2
  4. package/README.md +58 -20
  5. package/SECURITY.md +161 -120
  6. package/SUPPORT.md +1 -1
  7. package/browser-extension/broker-auth.js +83 -0
  8. package/browser-extension/broker-liveness.js +35 -0
  9. package/browser-extension/browser-error-boundary.js +96 -0
  10. package/browser-extension/browser-operations.js +1456 -101
  11. package/browser-extension/devtools-input.js +195 -34
  12. package/browser-extension/devtools-observation.js +429 -0
  13. package/browser-extension/devtools-session.js +48 -0
  14. package/browser-extension/manifest.json +4 -3
  15. package/browser-extension/page-automation.js +578 -118
  16. package/browser-extension/pairing-bootstrap.js +42 -0
  17. package/browser-extension/pairing.js +28 -12
  18. package/browser-extension/service-worker.js +126 -106
  19. package/docs/AGENT_CONTEXT.md +13 -10
  20. package/docs/ARCHITECTURE.md +159 -67
  21. package/docs/AUDIT.md +1302 -4
  22. package/docs/CLIENTS.md +19 -6
  23. package/docs/COMPUTER_USE.md +297 -0
  24. package/docs/ENGINEERING.md +72 -13
  25. package/docs/GETTING_STARTED.md +18 -20
  26. package/docs/LOCAL_AUTHORIZATION.md +115 -72
  27. package/docs/LOCAL_AUTOMATION.md +27 -21
  28. package/docs/LOGGING.md +40 -19
  29. package/docs/MANAGED_JOBS.md +37 -34
  30. package/docs/MULTI_ACCOUNT.md +98 -37
  31. package/docs/OPERATIONS.md +145 -51
  32. package/docs/OVERVIEW.md +14 -11
  33. package/docs/POLICY_REFERENCE.md +71 -3
  34. package/docs/PRIVACY.md +41 -2
  35. package/docs/PROJECT_STANDARDS.md +29 -6
  36. package/docs/RELEASING.md +238 -64
  37. package/docs/TESTING.md +150 -39
  38. package/docs/THREAT_MODEL.md +164 -67
  39. package/docs/TOOL_REFERENCE.md +653 -60
  40. package/docs/UPGRADING.md +78 -18
  41. package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
  42. package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
  43. package/native/macos/MachineBridgeTrustBroker.swift +233 -0
  44. package/package.json +94 -16
  45. package/scripts/accepted-candidate-tarball.mjs +77 -0
  46. package/scripts/candidate-runtime-store.mjs +93 -0
  47. package/scripts/check-plan.mjs +73 -3
  48. package/scripts/check-runner.mjs +129 -17
  49. package/scripts/commit-message-check.mjs +5 -2
  50. package/scripts/consumer-package-security.mjs +293 -0
  51. package/scripts/coverage-check.mjs +251 -15
  52. package/scripts/coverage-generation.mjs +41 -0
  53. package/scripts/coverage-range-merge.mjs +41 -0
  54. package/scripts/foreground-daemon-recovery.mjs +88 -0
  55. package/scripts/generate-policy-reference.mjs +28 -5
  56. package/scripts/generate-tool-reference.mjs +1 -1
  57. package/scripts/generate-worker-types.mjs +21 -11
  58. package/scripts/github-backlog.mjs +11 -10
  59. package/scripts/github-push.mjs +62 -16
  60. package/scripts/github-release-asset.mjs +58 -0
  61. package/scripts/github-release.mjs +241 -132
  62. package/scripts/global-package-installation.mjs +68 -0
  63. package/scripts/hardened-npm-session.mjs +42 -0
  64. package/scripts/install-published-prerelease.mjs +198 -0
  65. package/scripts/local-release-acceptance.mjs +103 -45
  66. package/scripts/macos-background-input-smoke.mjs +515 -0
  67. package/scripts/network-retry.mjs +8 -1
  68. package/scripts/npm-global-prefix.mjs +27 -0
  69. package/scripts/npm-publication-policy.mjs +17 -0
  70. package/scripts/official-mcp-conformance.mjs +267 -0
  71. package/scripts/persistent-activation-process.mjs +101 -0
  72. package/scripts/prepare-pinned-npm.mjs +12 -69
  73. package/scripts/prerelease-activation.mjs +160 -0
  74. package/scripts/privacy-check.mjs +14 -6
  75. package/scripts/promotion-digest.mjs +128 -0
  76. package/scripts/publish-npm.mjs +260 -0
  77. package/scripts/published-release.mjs +132 -0
  78. package/scripts/release-acceptance.mjs +62 -54
  79. package/scripts/release-browser-extension-store.mjs +172 -0
  80. package/scripts/release-candidate-manifest.mjs +52 -0
  81. package/scripts/release-channel.mjs +106 -0
  82. package/scripts/release-diagnostic.mjs +41 -0
  83. package/scripts/release-impact-check.mjs +12 -22
  84. package/scripts/release-oauth-canary-core.mjs +325 -0
  85. package/scripts/release-oauth-canary-evidence.mjs +100 -0
  86. package/scripts/release-oauth-canary.mjs +127 -0
  87. package/scripts/release-publication-guard.mjs +67 -0
  88. package/scripts/release-soak.mjs +284 -0
  89. package/scripts/release-state.mjs +1 -1
  90. package/scripts/run-checks.mjs +44 -7
  91. package/scripts/run-worker-dry-run.mjs +21 -0
  92. package/scripts/sarif-security-gate.mjs +2 -1
  93. package/scripts/sbom-check.mjs +106 -0
  94. package/scripts/start-release-candidate.mjs +267 -44
  95. package/scripts/syntax-check.mjs +36 -14
  96. package/scripts/verification-generation-guard.mjs +15 -0
  97. package/scripts/verification-idle-sleep-guard.mjs +37 -0
  98. package/scripts/verification-state.mjs +108 -0
  99. package/scripts/wrangler-command-lifecycle.mjs +115 -0
  100. package/src/local/account-access.mjs +13 -1
  101. package/src/local/account-admin-response.mjs +92 -0
  102. package/src/local/account-admin.mjs +52 -19
  103. package/src/local/agent-context-projection.mjs +26 -7
  104. package/src/local/agent-context.mjs +88 -49
  105. package/src/local/agent-skill-discovery.mjs +16 -5
  106. package/src/local/app-automation-macos-jxa.mjs +350 -0
  107. package/src/local/app-automation.mjs +873 -163
  108. package/src/local/application-capability-projection.mjs +24 -0
  109. package/src/local/authority-context.mjs +106 -0
  110. package/src/local/autostart-log-maintenance.mjs +36 -0
  111. package/src/local/browser-bridge-http.mjs +5 -3
  112. package/src/local/browser-bridge.mjs +109 -31
  113. package/src/local/browser-broker-auth-http.mjs +67 -0
  114. package/src/local/browser-broker-auth.mjs +98 -0
  115. package/src/local/browser-broker-routes.mjs +63 -20
  116. package/src/local/browser-broker-server.mjs +17 -7
  117. package/src/local/browser-command.mjs +79 -30
  118. package/src/local/browser-computer-observation-service.mjs +266 -0
  119. package/src/local/browser-extension-identity.mjs +50 -0
  120. package/src/local/browser-extension-path.mjs +22 -0
  121. package/src/local/browser-extension-protocol.mjs +50 -11
  122. package/src/local/browser-operation-service.mjs +147 -117
  123. package/src/local/browser-pairing-grant.mjs +109 -0
  124. package/src/local/browser-pairing-http.mjs +36 -0
  125. package/src/local/browser-pairing-launch.mjs +58 -0
  126. package/src/local/browser-pairing-store.mjs +85 -48
  127. package/src/local/browser-request-registry.mjs +40 -14
  128. package/src/local/browser-request-settlement.mjs +78 -0
  129. package/src/local/browser-resource-input.mjs +74 -0
  130. package/src/local/browser-trusted-input-health.mjs +63 -0
  131. package/src/local/call-authority.mjs +21 -0
  132. package/src/local/call-capacity.mjs +45 -0
  133. package/src/local/call-registry-drain.mjs +54 -0
  134. package/src/local/call-registry.mjs +30 -15
  135. package/src/local/capability-observer.mjs +5 -0
  136. package/src/local/capability-ranking.mjs +3 -2
  137. package/src/local/child-process-settlement.mjs +132 -0
  138. package/src/local/cli-account-admin.mjs +40 -6
  139. package/src/local/cli-activate.mjs +162 -0
  140. package/src/local/cli-local-admin.mjs +20 -45
  141. package/src/local/cli-options.mjs +12 -13
  142. package/src/local/cli-ready-output.mjs +52 -0
  143. package/src/local/cli-service.mjs +91 -11
  144. package/src/local/cli.mjs +232 -150
  145. package/src/local/computer-use-application-observation.mjs +224 -0
  146. package/src/local/computer-use-arguments.mjs +270 -0
  147. package/src/local/computer-use-deadline.mjs +50 -0
  148. package/src/local/computer-use-dispatch-settlement.mjs +40 -0
  149. package/src/local/computer-use-expectation.mjs +171 -0
  150. package/src/local/computer-use-observation.mjs +769 -0
  151. package/src/local/computer-use-recovery.mjs +82 -0
  152. package/src/local/computer-use-result-budget.mjs +145 -0
  153. package/src/local/computer-use-snapshot-store.mjs +67 -0
  154. package/src/local/computer-use.mjs +2255 -0
  155. package/src/local/daemon-http-relay-auth.mjs +26 -0
  156. package/src/local/daemon-http-relay-connection.mjs +298 -0
  157. package/src/local/daemon-http-relay-request.mjs +64 -0
  158. package/src/local/daemon-http-relay-sequence.mjs +49 -0
  159. package/src/local/daemon-process.mjs +40 -7
  160. package/src/local/delegated-process-sandbox.mjs +148 -0
  161. package/src/local/device-identity.mjs +156 -49
  162. package/src/local/device-root-provider.mjs +81 -0
  163. package/src/local/directory-metadata.mjs +47 -0
  164. package/src/local/doctor-reporting.mjs +23 -0
  165. package/src/local/durable-process-spec.mjs +80 -0
  166. package/src/local/errors.mjs +25 -15
  167. package/src/local/exclusive-file.mjs +74 -26
  168. package/src/local/execution-limits.mjs +9 -0
  169. package/src/local/execution-routing.mjs +276 -0
  170. package/src/local/execution-surface.mjs +23 -0
  171. package/src/local/file-mutation-coordinator.mjs +47 -0
  172. package/src/local/file-snapshot-preservation.mjs +24 -0
  173. package/src/local/filesystem-identity.mjs +54 -0
  174. package/src/local/fixed-process-environment.mjs +21 -0
  175. package/src/local/full-access-test.mjs +31 -13
  176. package/src/local/git-commit.mjs +69 -0
  177. package/src/local/git-config-safety.mjs +55 -0
  178. package/src/local/git-log-parser.mjs +41 -0
  179. package/src/local/git-metadata-boundary.mjs +77 -0
  180. package/src/local/git-metadata-tree-safety.mjs +59 -0
  181. package/src/local/git-operation-state.mjs +19 -0
  182. package/src/local/git-service.mjs +165 -30
  183. package/src/local/hardened-npm-download-timeout.mjs +46 -0
  184. package/src/local/hardened-npm-download.mjs +109 -0
  185. package/src/local/hardened-npm-extract.mjs +20 -0
  186. package/src/local/hardened-npm-verification.mjs +104 -0
  187. package/src/local/hardened-npm.mjs +199 -0
  188. package/src/local/job-runner.mjs +244 -89
  189. package/src/local/lifecycle.mjs +6 -0
  190. package/src/local/log.mjs +19 -45
  191. package/src/local/macos-background-input.mjs +398 -0
  192. package/src/local/macos-idle-sleep-assertion.mjs +65 -0
  193. package/src/local/macos-trust-broker.mjs +404 -0
  194. package/src/local/managed-job-cancellation.mjs +42 -0
  195. package/src/local/managed-job-capacity.mjs +16 -0
  196. package/src/local/managed-job-directory-generation.mjs +60 -0
  197. package/src/local/managed-job-directory.mjs +75 -0
  198. package/src/local/managed-job-durable-process.mjs +49 -0
  199. package/src/local/managed-job-lock.mjs +46 -14
  200. package/src/local/managed-job-plan.mjs +38 -14
  201. package/src/local/managed-job-projection.mjs +23 -15
  202. package/src/local/managed-job-retention.mjs +128 -0
  203. package/src/local/managed-job-runner-claim.mjs +70 -0
  204. package/src/local/managed-job-runner.mjs +35 -10
  205. package/src/local/managed-job-storage.mjs +22 -6
  206. package/src/local/managed-job-terminal-maintenance.mjs +40 -0
  207. package/src/local/managed-job-terminal.mjs +150 -0
  208. package/src/local/managed-jobs.mjs +424 -287
  209. package/src/local/npm-cli.mjs +42 -0
  210. package/src/local/npm-environment.mjs +37 -0
  211. package/src/local/operation-authorization.mjs +68 -324
  212. package/src/local/operation-risk.mjs +51 -4
  213. package/src/local/owner-state-lock.mjs +118 -0
  214. package/src/local/package-identity.mjs +9 -0
  215. package/src/local/patch.mjs +20 -18
  216. package/src/local/path-inspection.mjs +23 -0
  217. package/src/local/policy.mjs +19 -0
  218. package/src/local/private-toolchain-integrity.mjs +54 -0
  219. package/src/local/process-execution.mjs +87 -75
  220. package/src/local/process-foreground-timeout.mjs +50 -0
  221. package/src/local/process-identity.mjs +41 -7
  222. package/src/local/process-nonreplayable-settlement.mjs +50 -0
  223. package/src/local/process-result-projection.mjs +22 -0
  224. package/src/local/process-session-events.mjs +47 -0
  225. package/src/local/process-session-remote-activity.mjs +10 -0
  226. package/src/local/process-session-termination.mjs +73 -0
  227. package/src/local/process-sessions.mjs +144 -112
  228. package/src/local/process-tracker.mjs +140 -13
  229. package/src/local/process-tree-ownership-types.d.ts +37 -0
  230. package/src/local/process-tree-ownership.mjs +57 -0
  231. package/src/local/process-tree-signal.mjs +34 -0
  232. package/src/local/process-tree-snapshot.mjs +77 -0
  233. package/src/local/process-tree-supervisor.mjs +69 -0
  234. package/src/local/process-tree.mjs +11 -53
  235. package/src/local/project-metadata.mjs +19 -6
  236. package/src/local/relay-call-recovery.mjs +58 -38
  237. package/src/local/relay-connection-classification.mjs +131 -0
  238. package/src/local/relay-connection-support.mjs +91 -0
  239. package/src/local/relay-connection.mjs +179 -272
  240. package/src/local/relay-diagnostics.mjs +91 -0
  241. package/src/local/relay-heartbeat.mjs +119 -0
  242. package/src/local/relay-liveness.mjs +109 -0
  243. package/src/local/relay-peer-diagnostics.mjs +29 -0
  244. package/src/local/release-runtime-lock.mjs +18 -0
  245. package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
  246. package/src/local/remote-configuration.mjs +48 -0
  247. package/src/local/resilient-relay-connection.mjs +178 -0
  248. package/src/local/resource-admission-diagnostics.mjs +52 -0
  249. package/src/local/resource-admission-policy.mjs +115 -0
  250. package/src/local/resource-admission.mjs +339 -0
  251. package/src/local/resource-build-root.mjs +83 -0
  252. package/src/local/resource-cargo-concurrency.mjs +38 -0
  253. package/src/local/resource-cmake-concurrency.mjs +42 -0
  254. package/src/local/resource-command-concurrency.mjs +37 -0
  255. package/src/local/resource-command-profile.mjs +176 -0
  256. package/src/local/resource-coordinator-accounting.mjs +21 -0
  257. package/src/local/resource-cpu-window.mjs +20 -0
  258. package/src/local/resource-disk-headroom.mjs +13 -0
  259. package/src/local/resource-elastic-memory.mjs +19 -0
  260. package/src/local/resource-elastic-request.mjs +36 -0
  261. package/src/local/resource-foreground-wait.mjs +29 -0
  262. package/src/local/resource-go-concurrency.mjs +43 -0
  263. package/src/local/resource-gradle-concurrency.mjs +57 -0
  264. package/src/local/resource-host-cache.mjs +39 -0
  265. package/src/local/resource-host-darwin.mjs +54 -0
  266. package/src/local/resource-host-linux.mjs +35 -0
  267. package/src/local/resource-host-sample-file.mjs +23 -0
  268. package/src/local/resource-host-snapshot.mjs +99 -0
  269. package/src/local/resource-lease-accounting.mjs +119 -0
  270. package/src/local/resource-light-command.mjs +6 -0
  271. package/src/local/resource-make-concurrency.mjs +42 -0
  272. package/src/local/resource-maven-concurrency.mjs +38 -0
  273. package/src/local/resource-ninja-command-concurrency.mjs +20 -0
  274. package/src/local/resource-ninja-concurrency.mjs +28 -0
  275. package/src/local/resource-operations.mjs +26 -10
  276. package/src/local/resource-pressure.mjs +67 -0
  277. package/src/local/resource-probe-command.mjs +35 -0
  278. package/src/local/resource-process-admission.mjs +59 -0
  279. package/src/local/resource-process-ancestry-cache.mjs +43 -0
  280. package/src/local/resource-process-ancestry.mjs +32 -0
  281. package/src/local/resource-process-priority.mjs +15 -0
  282. package/src/local/resource-project-key.mjs +53 -0
  283. package/src/local/resource-pytest-concurrency.mjs +33 -0
  284. package/src/local/resource-release-control-classification.mjs +14 -0
  285. package/src/local/resource-release-control-executable.mjs +70 -0
  286. package/src/local/resource-release-control-workspace.mjs +41 -0
  287. package/src/local/resource-request-contract.mjs +30 -0
  288. package/src/local/resource-script-classification.mjs +44 -0
  289. package/src/local/resource-shell-analysis.mjs +58 -0
  290. package/src/local/resource-staging-recovery.mjs +97 -0
  291. package/src/local/resource-swift-concurrency.mjs +31 -0
  292. package/src/local/resource-transaction-lock.mjs +189 -0
  293. package/src/local/resource-wait.mjs +48 -0
  294. package/src/local/resource-waiters.mjs +139 -0
  295. package/src/local/resource-xcode-command.mjs +7 -0
  296. package/src/local/resource-xcode-concurrency.mjs +34 -0
  297. package/src/local/resource-xcode-non-build.mjs +19 -0
  298. package/src/local/runtime-activation.mjs +700 -0
  299. package/src/local/runtime-activity-projection.mjs +48 -0
  300. package/src/local/runtime-capabilities.mjs +48 -15
  301. package/src/local/runtime-diagnostic-state.mjs +14 -0
  302. package/src/local/runtime-diagnostics.mjs +40 -13
  303. package/src/local/runtime-info-projection.mjs +60 -0
  304. package/src/local/runtime-info-relay-projection.mjs +21 -0
  305. package/src/local/runtime-paths.mjs +2 -1
  306. package/src/local/runtime-process-routing.mjs +33 -0
  307. package/src/local/runtime-relay-connection-options.mjs +44 -0
  308. package/src/local/runtime-relay-control.mjs +98 -0
  309. package/src/local/runtime-relay.mjs +26 -50
  310. package/src/local/runtime-reporting.mjs +37 -21
  311. package/src/local/runtime-resource-service.mjs +77 -0
  312. package/src/local/runtime-tool-handlers.mjs +13 -10
  313. package/src/local/runtime.mjs +244 -208
  314. package/src/local/secure-file.mjs +73 -20
  315. package/src/local/security-audit-dispatch.mjs +35 -0
  316. package/src/local/security-audit-log.mjs +215 -0
  317. package/src/local/security-audit-state.mjs +139 -0
  318. package/src/local/security-audit-storage.mjs +135 -0
  319. package/src/local/security-audit-warning.mjs +31 -0
  320. package/src/local/security-audit-worker.mjs +87 -0
  321. package/src/local/service-convergence.mjs +42 -1
  322. package/src/local/service-definition.mjs +24 -0
  323. package/src/local/service-environment.mjs +18 -10
  324. package/src/local/service-owner.mjs +146 -0
  325. package/src/local/service-ownership.mjs +17 -0
  326. package/src/local/service-restart-handoff.mjs +64 -0
  327. package/src/local/service-restart-scheduler.mjs +49 -0
  328. package/src/local/service-runtime-convergence.mjs +52 -0
  329. package/src/local/service-runtime.mjs +105 -0
  330. package/src/local/service-status.mjs +60 -0
  331. package/src/local/service.mjs +385 -92
  332. package/src/local/shell.mjs +39 -19
  333. package/src/local/ssh-key.mjs +158 -42
  334. package/src/local/state-inventory.mjs +50 -24
  335. package/src/local/state-owner-lock-inventory.mjs +23 -0
  336. package/src/local/state-root-owned-namespaces.mjs +83 -0
  337. package/src/local/state-root-retirement.mjs +89 -0
  338. package/src/local/state.mjs +386 -96
  339. package/src/local/stdio.mjs +96 -66
  340. package/src/local/support-state-projection.mjs +30 -0
  341. package/src/local/system-network-route.mjs +76 -0
  342. package/src/local/systemd-removal.mjs +20 -0
  343. package/src/local/tool-executor.mjs +85 -20
  344. package/src/local/tool-result-boundary.mjs +51 -0
  345. package/src/local/toolchain-operation-lock.mjs +41 -0
  346. package/src/local/tools.mjs +7 -5
  347. package/src/local/trusted-executable.mjs +53 -0
  348. package/src/local/trusted-git-executable.mjs +34 -0
  349. package/src/local/trusted-github-cli.mjs +24 -0
  350. package/src/local/windows-service-convergence.mjs +49 -0
  351. package/src/local/windows-service.mjs +52 -45
  352. package/src/local/worker-deployment-fingerprint.mjs +102 -0
  353. package/src/local/worker-deployment.mjs +48 -60
  354. package/src/local/worker-health.mjs +12 -4
  355. package/src/local/worker-secret-file.mjs +37 -17
  356. package/src/local/workspace-file-service.mjs +156 -239
  357. package/src/local/workspace-file-transaction.mjs +189 -0
  358. package/src/local/workspace-search.mjs +55 -0
  359. package/src/local/wrangler-toolchain/package-lock.json +1505 -0
  360. package/src/local/wrangler-toolchain/package.json +23 -0
  361. package/src/local/wrangler-toolchain-verification.mjs +135 -0
  362. package/src/local/wrangler-toolchain.mjs +188 -0
  363. package/src/shared/access-contract.json +7 -1
  364. package/src/shared/activation-recovery.mjs +49 -0
  365. package/src/shared/admin-auth.d.mts +2 -1
  366. package/src/shared/admin-auth.mjs +3 -3
  367. package/src/shared/authority-revocation.d.mts +9 -0
  368. package/src/shared/authority-revocation.mjs +28 -0
  369. package/src/shared/daemon-auth.d.mts +11 -0
  370. package/src/shared/daemon-auth.mjs +17 -0
  371. package/src/shared/device-session-auth.d.mts +20 -0
  372. package/src/shared/device-session-auth.mjs +51 -0
  373. package/src/shared/foreground-timeout.d.mts +15 -0
  374. package/src/shared/foreground-timeout.mjs +70 -0
  375. package/src/shared/log-redaction.d.mts +9 -0
  376. package/src/shared/log-redaction.mjs +51 -0
  377. package/src/shared/mcp-protocol.d.mts +27 -0
  378. package/src/shared/mcp-protocol.mjs +275 -0
  379. package/src/shared/project-overview-projection.d.mts +4 -0
  380. package/src/shared/project-overview-projection.mjs +96 -0
  381. package/src/shared/relay-contract.json +28 -0
  382. package/src/shared/result-projection.d.mts +2 -1
  383. package/src/shared/result-projection.mjs +13 -2
  384. package/src/shared/server-metadata.json +15 -9
  385. package/src/shared/tool-argument-validation.d.mts +17 -0
  386. package/src/shared/tool-argument-validation.mjs +325 -0
  387. package/src/shared/tool-call-capacity.d.mts +41 -0
  388. package/src/shared/tool-call-capacity.mjs +59 -0
  389. package/src/shared/tool-catalog.json +626 -57
  390. package/src/worker/access.ts +12 -0
  391. package/src/worker/account-admin.ts +55 -33
  392. package/src/worker/authority-revocations.ts +174 -0
  393. package/src/worker/authority.ts +52 -6
  394. package/src/worker/daemon-auth.ts +49 -68
  395. package/src/worker/daemon-channel.ts +36 -0
  396. package/src/worker/daemon-http-auth.ts +63 -0
  397. package/src/worker/daemon-http-channel.ts +120 -0
  398. package/src/worker/daemon-http-controller.ts +163 -0
  399. package/src/worker/daemon-http-protocol.ts +62 -0
  400. package/src/worker/daemon-http-registry.ts +82 -0
  401. package/src/worker/daemon-last-observation.ts +30 -0
  402. package/src/worker/daemon-liveness.ts +8 -6
  403. package/src/worker/daemon-ready-dispatch.ts +17 -0
  404. package/src/worker/daemon-ready-messages.ts +73 -0
  405. package/src/worker/daemon-ready-waiters.ts +79 -0
  406. package/src/worker/daemon-recovery-budget.ts +13 -0
  407. package/src/worker/daemon-registry.ts +58 -0
  408. package/src/worker/daemon-relay-diagnostics.ts +127 -0
  409. package/src/worker/daemon-socket-attachment.ts +55 -0
  410. package/src/worker/daemon-sockets.ts +72 -62
  411. package/src/worker/daemon-status.ts +38 -0
  412. package/src/worker/device-session-verifier.ts +129 -0
  413. package/src/worker/dpop.ts +157 -0
  414. package/src/worker/durable-process-timeout.ts +29 -0
  415. package/src/worker/errors.ts +57 -0
  416. package/src/worker/http.ts +112 -26
  417. package/src/worker/index.ts +446 -440
  418. package/src/worker/mcp-access.ts +57 -0
  419. package/src/worker/mcp-controller.ts +188 -0
  420. package/src/worker/mcp-http-accept.ts +11 -0
  421. package/src/worker/mcp-http-contract.ts +322 -0
  422. package/src/worker/mcp-initialization-compat.ts +141 -0
  423. package/src/worker/mcp-jsonrpc.ts +11 -29
  424. package/src/worker/mcp-removed-protocol.ts +17 -0
  425. package/src/worker/mcp-response-proxy.ts +135 -0
  426. package/src/worker/mcp-response-stream.ts +65 -0
  427. package/src/worker/mcp-stale-schema-compat.ts +57 -0
  428. package/src/worker/mcp-stream-proxy-contract.ts +48 -0
  429. package/src/worker/mcp-tool-call-input.ts +23 -0
  430. package/src/worker/nonce-store.ts +33 -35
  431. package/src/worker/oauth-authorization-page.ts +21 -1
  432. package/src/worker/oauth-client-admin.ts +52 -0
  433. package/src/worker/oauth-client-contract.ts +38 -0
  434. package/src/worker/oauth-controller.ts +107 -47
  435. package/src/worker/oauth-field-contract.ts +31 -0
  436. package/src/worker/oauth-record-contract.ts +7 -0
  437. package/src/worker/oauth-refresh-authority.ts +27 -0
  438. package/src/worker/oauth-refresh-exchange.ts +162 -0
  439. package/src/worker/oauth-refresh-families.ts +130 -27
  440. package/src/worker/oauth-refresh-persistence.ts +116 -0
  441. package/src/worker/oauth-state.ts +33 -18
  442. package/src/worker/oauth-store-validation.ts +131 -0
  443. package/src/worker/oauth-token-derivation.ts +33 -0
  444. package/src/worker/oauth-token-issuance.ts +119 -0
  445. package/src/worker/oauth-tokens.ts +56 -190
  446. package/src/worker/observability.ts +38 -5
  447. package/src/worker/pending-admission.ts +15 -0
  448. package/src/worker/pending-call-capacity.ts +108 -0
  449. package/src/worker/pending-call-contract.ts +42 -5
  450. package/src/worker/pending-call-deadlines.ts +75 -0
  451. package/src/worker/pending-calls.ts +131 -125
  452. package/src/worker/runtime-alarm-storage.ts +37 -0
  453. package/src/worker/runtime-alarm.ts +136 -0
  454. package/src/worker/server-info-activity.ts +36 -0
  455. package/src/worker/server-info-tool-delivery.ts +15 -0
  456. package/src/worker/server-info.ts +168 -0
  457. package/src/worker/tool-catalog.ts +47 -1
  458. package/src/worker/tool-timeout.ts +75 -14
  459. package/src/worker/websocket-protocol.ts +26 -2
  460. package/src/worker/worker-edge-guard.ts +90 -0
  461. package/src/worker/worker-edge-log.ts +65 -0
  462. package/src/worker/worker-entry.ts +52 -0
  463. package/src/worker/worker-mcp-config.ts +23 -0
  464. package/src/worker/worker-metadata.ts +48 -0
  465. package/src/worker/worker-rate-limit-key.ts +30 -0
  466. package/src/worker/worker-runtime-config.ts +20 -0
  467. package/src/worker/worker-static-routes.ts +54 -0
  468. package/src/worker/worker-task-lifetime.ts +18 -0
  469. package/tsconfig.local.json +17 -1
  470. package/wrangler.jsonc +13 -0
  471. package/src/local/cli-approval.mjs +0 -117
  472. package/src/local/operation-state-lock.mjs +0 -92
  473. package/src/worker/mcp-session.ts +0 -72
@@ -0,0 +1,42 @@
1
+ (() => {
2
+ const GRANT = /^(\d{13})\.([A-Za-z0-9_-]{22})\.([A-Za-z0-9_-]{43})$/;
3
+ const PAIRING_GRANT_TTL_MS = 30_000;
4
+
5
+ async function bootstrapPairing(port, grant, now = Date.now()) {
6
+ const auth = globalThis.__machineBridgeBrokerAuth?.internal;
7
+ if (!auth) throw new Error("browser broker authentication module is unavailable");
8
+ const normalizedPort = Number(port);
9
+ const match = GRANT.exec(String(grant || ""));
10
+ const current = Number(now);
11
+ if (!Number.isInteger(normalizedPort) || normalizedPort < 1024 || normalizedPort > 65535 || !match || !Number.isFinite(current)) {
12
+ throw new Error("browser pairing bootstrap is invalid");
13
+ }
14
+ const expiresAt = Number(match[1]);
15
+ if (expiresAt < current || expiresAt - current > PAIRING_GRANT_TTL_MS) throw new Error("browser pairing bootstrap expired");
16
+ const grantId = `${match[1]}.${match[2]}`;
17
+ const secret = match[3];
18
+ const clientChallenge = auth.randomBase64Url(24);
19
+ const authUrl = new URL(`http://127.0.0.1:${normalizedPort}/pair-auth`);
20
+ authUrl.searchParams.set("grant", grantId);
21
+ authUrl.searchParams.set("challenge", clientChallenge);
22
+ const initProof = await auth.hmac(secret, `machine-bridge-browser-pair-init-v2\0${grantId}\0${clientChallenge}`);
23
+ authUrl.searchParams.set("init", initProof);
24
+ const first = await auth.brokerFetch(authUrl, "GET");
25
+ if (first.status !== 204) throw new Error("browser pairing broker authentication failed");
26
+ const serverNonce = String(first.headers.get("x-machine-bridge-broker-nonce") || "");
27
+ const serverProof = String(first.headers.get("x-machine-bridge-broker-proof") || "");
28
+ if (!auth.noncePattern.test(serverNonce) || !auth.proofPattern.test(serverProof)) throw new Error("browser pairing broker authentication failed");
29
+ const expected = await auth.hmac(secret, `machine-bridge-browser-pair-server-v2\0${grantId}\0${clientChallenge}\0${serverNonce}`);
30
+ if (!auth.fixedEqual(expected, serverProof)) throw new Error("browser pairing broker authentication failed");
31
+ const clientProof = await auth.hmac(secret, `machine-bridge-browser-pair-client-v2\0${grantId}\0${clientChallenge}\0${serverNonce}`);
32
+ authUrl.searchParams.set("nonce", serverNonce);
33
+ authUrl.searchParams.set("proof", clientProof);
34
+ const second = await auth.brokerFetch(authUrl, "POST");
35
+ if (second.status !== 204) throw new Error("browser pairing broker authentication failed");
36
+ const token = String(second.headers.get("x-machine-bridge-extension-token") || "");
37
+ if (!auth.tokenPattern.test(token)) throw new Error("browser pairing broker returned an invalid extension credential");
38
+ return { endpoint: `ws://127.0.0.1:${normalizedPort}/extension`, token };
39
+ }
40
+
41
+ globalThis.__machineBridgePairingBootstrap = Object.freeze({ bootstrapPairing });
42
+ })();
@@ -1,19 +1,35 @@
1
1
  (() => {
2
- const marker = document.querySelector('meta[name="machine-bridge-browser-pair"]');
3
- const portMeta = document.querySelector('meta[name="machine-bridge-browser-port"]');
4
- const tokenMeta = document.querySelector('meta[name="machine-bridge-browser-token"]');
5
- if (marker?.content !== "1" || !portMeta || !tokenMeta) return;
6
- const token = tokenMeta.content;
7
- const port = Number(portMeta.content);
8
- if (!/^[A-Za-z0-9_-]{32,100}$/.test(token) || !Number.isInteger(port) || port < 1024 || port > 65535) return;
9
- chrome.runtime.sendMessage({ type: "pair", endpoint: `ws://127.0.0.1:${port}/extension`, token }, (response) => {
10
- const status = document.getElementById("status");
2
+ let parsed;
3
+ try { parsed = new URL(location.href); } catch { return; }
4
+ const pagePort = Number(parsed.port);
5
+ const fragment = parsed.hash.startsWith("#") ? new URLSearchParams(parsed.hash.slice(1)) : null;
6
+ const brokerPort = Number(fragment?.get("broker_port"));
7
+ const grant = String(fragment?.get("grant") || "");
8
+ if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/pair"
9
+ || parsed.username || parsed.password || parsed.search || fragment?.size !== 2
10
+ || !Number.isInteger(pagePort) || pagePort < 1024 || pagePort > 65535
11
+ || !Number.isInteger(brokerPort) || brokerPort < 1024 || brokerPort > 65535
12
+ || !/^\d{13}\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}$/.test(grant)) return;
13
+ try { history.replaceState(null, "", parsed.pathname); } catch { return; }
14
+ if (location.hash) return;
15
+
16
+ const updateStatus = (text) => {
17
+ const apply = () => { const status = document.getElementById("status"); if (status) status.textContent = text; };
18
+ if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", apply, { once: true });
19
+ else apply();
20
+ };
21
+ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
22
+ if (message?.type === "machine_bridge_pairing_material") { sendResponse({ port: brokerPort, grant }); return false; }
23
+ if (message?.type === "machine_bridge_pairing_status") { updateStatus(String(message.text || "")); return false; }
24
+ return false;
25
+ });
26
+ chrome.runtime.sendMessage({ type: "pair_bootstrap", port: brokerPort, grant }, (response) => {
11
27
  if (chrome.runtime.lastError || response?.ok !== true) {
12
- if (status) status.textContent = response?.requires_manual_repair
28
+ updateStatus(response?.requires_manual_repair
13
29
  ? "This extension is already paired to different local state. Click the Machine Bridge extension icon while this page is active to confirm re-pairing."
14
- : "Pairing failed. Confirm the local Machine Bridge runtime is running, then reload this page.";
30
+ : "Pairing failed. Run pair_browser_extension with opening enabled again.");
15
31
  return;
16
32
  }
17
- if (status) status.textContent = "Paired. You may close this tab.";
33
+ updateStatus("Paired. You may close this tab.");
18
34
  });
19
35
  })();
@@ -1,63 +1,59 @@
1
- importScripts("devtools-input.js", "browser-operations.js");
2
-
1
+ importScripts("browser-error-boundary.js", "broker-auth.js", "pairing-bootstrap.js", "broker-liveness.js", "devtools-session.js", "devtools-input.js", "devtools-observation.js", "browser-operations.js"); const brokerLiveness = globalThis.__machineBridgeBrokerLiveness;
3
2
  let socket = null;
4
3
  let reconnectTimer = null;
5
4
  let reconnectAttempt = 0;
6
- let keepaliveTimer = null;
5
+ let connectGeneration = 0;
6
+ let authenticating = false;
7
7
  const MAX_RESULT_BYTES = 7 * 1024 * 1024;
8
8
  const BROWSER_EXTENSION_PROTOCOL = 3;
9
9
  const HANDSHAKE_TIMEOUT_MS = 3000;
10
+ const MAX_ACTIVE_REQUESTS = 32;
10
11
  const activeRequests = new Map();
11
-
12
12
  chrome.runtime.onInstalled.addListener(() => { ensureReconnectAlarm(); void connectFromStorage(); });
13
13
  chrome.runtime.onStartup.addListener(() => { ensureReconnectAlarm(); void connectFromStorage(); });
14
14
  chrome.alarms.onAlarm.addListener((alarm) => {
15
15
  if (alarm.name === "machine-bridge-reconnect") void connectFromStorage();
16
16
  });
17
17
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
18
- if (message?.type !== "pair") return false;
19
- pairConfiguration(message.endpoint, message.token, { replace: false, senderUrl: sender.url || "" })
18
+ if (message?.type === "machine_bridge_internal_delay") {
19
+ if (sender?.id !== chrome.runtime.id) return false;
20
+ const delayMs = message.delay_ms;
21
+ if (!Number.isSafeInteger(delayMs) || delayMs < 1 || delayMs > 250) {
22
+ sendResponse({ ok: false });
23
+ return false;
24
+ }
25
+ setTimeout(() => sendResponse({ ok: true }), delayMs);
26
+ return true;
27
+ }
28
+ if (message?.type !== "pair_bootstrap") return false;
29
+ pairFromBootstrap(message.port, message.grant, { replace: false })
20
30
  .then(sendResponse)
21
31
  .catch(() => sendResponse({ ok: false }));
22
32
  return true;
23
33
  });
24
34
  chrome.action.onClicked.addListener((tab) => void handleActionClick(tab));
25
-
26
35
  async function handleActionClick(tab) {
27
- if (tab?.id && parsePairingPage(tab.url)) {
28
- await confirmRepairFromTab(tab);
36
+ const pairingPage = tab?.id ? browserBrokerAuth().parsePairingPage(tab.url) : null;
37
+ if (pairingPage) {
38
+ try {
39
+ const material = await chrome.tabs.sendMessage(tab.id, { type: "machine_bridge_pairing_material" });
40
+ if (material?.grant && Number.isInteger(Number(material.port))) {
41
+ const paired = await pairFromBootstrap(material.port, material.grant, { replace: true });
42
+ await setPairingPageStatus(tab.id, paired.ok === true ? "Paired. You may close this tab." : "Pairing failed.");
43
+ return;
44
+ }
45
+ } catch {
46
+ // A token-free or stale pairing tab has no isolated bootstrap material; fall through to the explicit re-pair instruction.
47
+ }
48
+ await setPairingPageStatus(tab.id, "Pairing grant unavailable. Run pair_browser_extension with opening enabled again.")
49
+ .catch(() => { /* The stale pairing tab may already be gone; status decoration is best-effort. */ });
29
50
  return;
30
51
  }
31
52
  const current = await chrome.storage.local.get(["endpoint"]);
32
- const pairUrl = pairingUrlFromEndpoint(current.endpoint) || "http://127.0.0.1:39393/pair";
53
+ const pairUrl = browserBrokerAuth().pairingUrlFromEndpoint(current.endpoint) || "http://127.0.0.1:39393/pair";
33
54
  await chrome.tabs.create({ url: pairUrl });
34
55
  }
35
56
 
36
- function pairingUrlFromEndpoint(endpoint) {
37
- const parsed = parseBrokerEndpoint(endpoint);
38
- return parsed ? `http://127.0.0.1:${parsed.port}/pair` : "";
39
- }
40
-
41
- function parsePairingPage(value) {
42
- let parsed;
43
- try { parsed = new URL(String(value || "")); } catch { return null; }
44
- const port = Number(parsed.port);
45
- if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/pair"
46
- || parsed.username || parsed.password || parsed.search || parsed.hash
47
- || !Number.isInteger(port) || port < 1024 || port > 65535) return null;
48
- return parsed;
49
- }
50
-
51
- function parseBrokerEndpoint(value) {
52
- let parsed;
53
- try { parsed = new URL(String(value || "")); } catch { return null; }
54
- const port = Number(parsed.port);
55
- if (parsed.protocol !== "ws:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/extension"
56
- || parsed.username || parsed.password || parsed.search || parsed.hash
57
- || !Number.isInteger(port) || port < 1024 || port > 65535) return null;
58
- return parsed;
59
- }
60
-
61
57
  function setConnectionState(state) {
62
58
  const badge = state === "connected" ? "ON" : state === "connecting" ? "..." : state === "unconfigured" ? "?" : "!";
63
59
  const title = state === "connected"
@@ -74,7 +70,7 @@ function setConnectionState(state) {
74
70
  function ignoreBrowserApiCall(operation) {
75
71
  try {
76
72
  const value = operation();
77
- if (value && typeof value.catch === "function") value.catch(() => {});
73
+ if (value && typeof value.catch === "function") value.catch(() => { /* Browser UI decoration is optional. */ });
78
74
  } catch {
79
75
  // Browser UI decoration is optional and must not disrupt broker connectivity.
80
76
  }
@@ -91,18 +87,24 @@ function closeSocketQuietly(ws, code, reason) {
91
87
  function sendSocketQuietly(ws, payload) {
92
88
  try {
93
89
  ws.send(payload);
90
+ return true;
94
91
  } catch {
95
92
  // A response cannot be recovered after the broker socket closes.
93
+ return false;
96
94
  }
97
95
  }
98
96
 
99
- async function pairConfiguration(rawEndpoint, rawToken, { replace, senderUrl }) {
97
+ async function pairFromBootstrap(port, grant, { replace }) {
98
+ const bootstrap = browserPairingBootstrap();
99
+ const candidate = await bootstrap.bootstrapPairing(port, grant);
100
+ return pairConfiguration(candidate.endpoint, candidate.token, { replace });
101
+ }
102
+
103
+ async function pairConfiguration(rawEndpoint, rawToken, { replace }) {
100
104
  const endpoint = String(rawEndpoint || "");
101
105
  const token = String(rawToken || "");
102
- const pairingPage = parsePairingPage(senderUrl);
103
- const brokerEndpoint = parseBrokerEndpoint(endpoint);
104
- if (!pairingPage) return { ok: false, error: "invalid_pairing_page" };
105
- if (!brokerEndpoint || pairingPage.port !== brokerEndpoint.port || !/^[A-Za-z0-9_-]{32,100}$/.test(token)) return { ok: false, error: "invalid_pairing_material" };
106
+ const brokerEndpoint = browserBrokerAuth().parseBrokerEndpoint(endpoint);
107
+ if (!brokerEndpoint || !/^[A-Za-z0-9_-]{32,100}$/.test(token)) return { ok: false, error: "invalid_pairing_material" };
106
108
  const current = await chrome.storage.local.get(["endpoint", "token"]);
107
109
  const alreadyPaired = Boolean(current.endpoint && current.token);
108
110
  const samePairing = current.endpoint === endpoint && current.token === token;
@@ -113,54 +115,32 @@ async function pairConfiguration(rawEndpoint, rawToken, { replace, senderUrl })
113
115
  && socket.machineBridgeToken === token) {
114
116
  return { ok: true, replaced: false, already_connected: true };
115
117
  }
116
- if (alreadyPaired && !replace && (current.endpoint !== endpoint || current.token !== token)) {
117
- return { ok: false, requires_manual_repair: true };
118
- }
118
+ if (alreadyPaired && !replace && !samePairing) return { ok: false, requires_manual_repair: true };
119
119
  setConnectionState("connecting");
120
120
  try {
121
121
  const connectedSocket = await connect(endpoint, token, { reconnect: false });
122
122
  await chrome.storage.local.set({ endpoint, token });
123
123
  connectedSocket.reconnectEnabled = true;
124
- if (connectedSocket.readyState !== WebSocket.OPEN || connectedSocket.bridgeReady !== true) void connect(endpoint, token).catch(() => {});
125
- return { ok: true, replaced: alreadyPaired && (current.endpoint !== endpoint || current.token !== token) };
124
+ if (connectedSocket.readyState !== WebSocket.OPEN || connectedSocket.bridgeReady !== true) {
125
+ void connect(endpoint, token).catch(() => { /* The reconnect state machine owns subsequent retries. */ });
126
+ }
127
+ return { ok: true, replaced: alreadyPaired && !samePairing };
126
128
  } catch (error) {
127
- if (current.endpoint && current.token) void connect(current.endpoint, current.token).catch(() => {});
129
+ if (current.endpoint && current.token) {
130
+ void connect(current.endpoint, current.token).catch(() => { /* Preserve the primary pairing error; prior-pair recovery is best-effort. */ });
131
+ }
128
132
  throw error;
129
133
  }
130
134
  }
131
135
 
132
- async function confirmRepairFromTab(tab) {
133
- if (!tab?.id || !parsePairingPage(tab.url)) return;
134
- try {
135
- const [result] = await chrome.scripting.executeScript({
136
- target: { tabId: tab.id },
137
- func: () => ({
138
- endpoint: `ws://127.0.0.1:${document.querySelector('meta[name="machine-bridge-browser-port"]')?.content || ""}/extension`,
139
- token: document.querySelector('meta[name="machine-bridge-browser-token"]')?.content || "",
140
- }),
141
- });
142
- const paired = await pairConfiguration(result?.result?.endpoint, result?.result?.token, { replace: true, senderUrl: tab.url });
143
- await setPairingPageStatus(
144
- tab.id,
145
- paired.ok === true ? "Paired. You may close this tab." : "Pairing failed.",
146
- );
147
- } catch {
148
- ignoreBrowserApiCall(() => setPairingPageStatus(
149
- tab.id,
150
- "Pairing failed. Reload this page and confirm that the expected extension build is loaded.",
151
- ));
152
- }
136
+ function setPairingPageStatus(tabId, text) {
137
+ return chrome.tabs.sendMessage(tabId, { type: "machine_bridge_pairing_status", text });
153
138
  }
154
139
 
155
- function setPairingPageStatus(tabId, text) {
156
- return chrome.scripting.executeScript({
157
- target: { tabId },
158
- func: (value) => {
159
- const status = document.getElementById("status");
160
- if (status) status.textContent = value;
161
- },
162
- args: [text],
163
- });
140
+ function browserPairingBootstrap() {
141
+ const bootstrap = globalThis.__machineBridgePairingBootstrap;
142
+ if (!bootstrap || typeof bootstrap.bootstrapPairing !== "function") throw new Error("browser pairing bootstrap module is unavailable");
143
+ return bootstrap;
164
144
  }
165
145
 
166
146
  ensureReconnectAlarm();
@@ -171,30 +151,44 @@ function ensureReconnectAlarm() {
171
151
  }
172
152
 
173
153
  async function connectFromStorage() {
174
- if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) return;
154
+ if (authenticating || (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING))) return;
175
155
  const value = await chrome.storage.local.get(["endpoint", "token"]);
176
156
  if (value.endpoint && value.token) {
177
157
  setConnectionState("connecting");
178
- void connect(value.endpoint, value.token).catch(() => {});
158
+ void connect(value.endpoint, value.token).catch(() => { /* Connection close/retry state is reflected by the socket lifecycle. */ });
179
159
  } else {
180
160
  setConnectionState("unconfigured");
181
161
  }
182
162
  }
183
163
 
184
- function connect(endpoint, token, { reconnect = true } = {}) {
164
+ async function connect(endpoint, token, { reconnect = true } = {}) {
165
+ const generation = ++connectGeneration;
185
166
  clearTimeout(reconnectTimer);
186
167
  reconnectTimer = null;
187
168
  if (socket) {
169
+ socket.reconnectEnabled = false;
170
+ clearInterval(socket.keepaliveTimer);
171
+ socket.keepaliveTimer = null;
172
+ brokerLiveness.clear(socket);
173
+ cancelRequestsForSocket(socket);
188
174
  closeSocketQuietly(socket);
175
+ socket = null;
189
176
  }
190
- const ws = new WebSocket(endpoint, [`mbm.${token}`]);
177
+ authenticating = true;
178
+ setConnectionState("connecting");
179
+ let protocol;
180
+ try { protocol = await extensionBrokerProtocol(endpoint, token); }
181
+ finally { if (generation === connectGeneration) authenticating = false; }
182
+ if (generation !== connectGeneration) throw new Error("browser connection was superseded");
183
+ const ws = new WebSocket(endpoint, [protocol]);
191
184
  socket = ws;
192
185
  ws.bridgeReady = false;
193
186
  ws.serverHelloSeen = false;
194
187
  ws.machineBridgeEndpoint = endpoint;
195
188
  ws.machineBridgeToken = token;
196
189
  ws.reconnectEnabled = reconnect;
197
- setConnectionState("connecting");
190
+ ws.keepaliveTimer = null;
191
+ brokerLiveness.initialize(ws);
198
192
  return new Promise((resolvePromise, rejectPromise) => {
199
193
  let settled = false;
200
194
  const settle = (error = null) => {
@@ -213,14 +207,15 @@ function connect(endpoint, token, { reconnect = true } = {}) {
213
207
  }, HANDSHAKE_TIMEOUT_MS);
214
208
  };
215
209
  ws.onmessage = (event) => void handleMessage(ws, event.data, () => settle());
216
- ws.onerror = () => {};
210
+ ws.onerror = () => { /* WebSocket close owns failure settlement and reconnect classification. */ };
217
211
  ws.onclose = () => {
218
212
  clearTimeout(ws.handshakeTimer);
213
+ clearInterval(ws.keepaliveTimer);
214
+ brokerLiveness.clear(ws);
215
+ ws.keepaliveTimer = null;
216
+ cancelRequestsForSocket(ws);
219
217
  if (!ws.bridgeReady) settle(new Error("browser broker handshake failed"));
220
218
  if (socket !== ws) return;
221
- clearInterval(keepaliveTimer);
222
- keepaliveTimer = null;
223
- cancelRequestsForSocket(ws);
224
219
  socket = null;
225
220
  setConnectionState("disconnected");
226
221
  if (ws.reconnectEnabled) scheduleReconnect(endpoint, token);
@@ -228,10 +223,20 @@ function connect(endpoint, token, { reconnect = true } = {}) {
228
223
  });
229
224
  }
230
225
 
226
+ function browserBrokerAuth() {
227
+ const auth = globalThis.__machineBridgeBrokerAuth;
228
+ if (!auth || typeof auth.extensionProtocol !== "function") throw new Error("browser broker authentication module is unavailable");
229
+ return auth;
230
+ }
231
+
232
+ function extensionBrokerProtocol(endpoint, token) { return browserBrokerAuth().extensionProtocol(endpoint, token); }
233
+
231
234
  function scheduleReconnect(endpoint, token) {
232
235
  clearTimeout(reconnectTimer);
233
236
  const delay = Math.min(30000, 1000 * 2 ** Math.min(reconnectAttempt++, 5));
234
- reconnectTimer = setTimeout(() => { void connect(endpoint, token).catch(() => {}); }, delay);
237
+ reconnectTimer = setTimeout(() => {
238
+ void connect(endpoint, token).catch(() => { /* A failed attempt schedules or awaits the next reconnect trigger. */ });
239
+ }, delay);
235
240
  }
236
241
 
237
242
  async function handleMessage(ws, raw, onReady = () => {}) {
@@ -247,15 +252,16 @@ async function handleMessage(ws, raw, onReady = () => {}) {
247
252
  }
248
253
  ws.serverHelloSeen = true;
249
254
  const manifest = chrome.runtime.getManifest();
250
- ws.send(JSON.stringify({
255
+ const helloSent = sendSocketQuietly(ws, JSON.stringify({
251
256
  type: "hello",
252
257
  role: "extension",
253
258
  protocol: BROWSER_EXTENSION_PROTOCOL,
254
259
  version: manifest.version_name || manifest.version,
255
- capabilities: [
256
- "semantic_snapshot_refs", "actionability_waits", "trusted_input", "tab_management", "explicit_waits",
257
- ],
260
+ extension_id: chrome.runtime.id,
261
+ capabilities: ["semantic_snapshot_refs", "actionability_waits", "trusted_input", "tab_management", "explicit_waits",
262
+ "cdp_accessibility_snapshot", "cdp_surface_screenshot", "computer_observation_v1", "backend_node_trusted_input"],
258
263
  }));
264
+ if (!helloSent) closeSocketQuietly(ws, 1011, "browser extension hello failed");
259
265
  return;
260
266
  }
261
267
  if (message?.type === "hello_ack") {
@@ -265,12 +271,13 @@ async function handleMessage(ws, raw, onReady = () => {}) {
265
271
  }
266
272
  clearTimeout(ws.handshakeTimer);
267
273
  ws.bridgeReady = true;
274
+ brokerLiveness.negotiate(ws, message.pong_watchdog);
268
275
  reconnectAttempt = 0;
269
276
  setConnectionState("connected");
270
- clearInterval(keepaliveTimer);
271
- keepaliveTimer = setInterval(() => {
272
- if (socket === ws && ws.bridgeReady && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: "ping" }));
273
- }, 20000);
277
+ clearInterval(ws.keepaliveTimer);
278
+ ws.keepaliveTimer = setInterval(() => brokerLiveness.keepalive(
279
+ ws, socket === ws, sendSocketQuietly, closeSocketQuietly,
280
+ ), 20000);
274
281
  onReady();
275
282
  return;
276
283
  }
@@ -278,21 +285,36 @@ async function handleMessage(ws, raw, onReady = () => {}) {
278
285
  closeSocketQuietly(ws, 1002, "browser broker acknowledgement required");
279
286
  return;
280
287
  }
288
+ if (message?.type === "pong") return brokerLiveness.handlePong(ws, message, closeSocketQuietly);
281
289
  if (message?.type === "cancel" && typeof message.id === "string") {
282
290
  const state = activeRequests.get(message.id);
283
291
  if (state) state.cancelled = true;
284
292
  return;
285
293
  }
286
294
  if (message?.type !== "request" || typeof message.id !== "string" || typeof message.method !== "string") return;
295
+ if (activeRequests.has(message.id)) {
296
+ closeSocketQuietly(ws, 1002, "duplicate browser request id");
297
+ return;
298
+ }
299
+ if (activeRequests.size >= MAX_ACTIVE_REQUESTS) {
300
+ if (!sendResponse(ws, message.id, false, null, "too many concurrent browser requests")) {
301
+ closeSocketQuietly(ws, 1011, "browser overload response delivery failed");
302
+ }
303
+ return;
304
+ }
287
305
  const state = { cancelled: false, timeoutMs: browserOperations().boundedRequestTimeout(message.timeout_ms), socket: ws };
288
306
  activeRequests.set(message.id, state);
289
307
  try {
290
308
  throwIfCancelled(state);
291
309
  const result = await dispatch(message.method, message.params || {}, state);
292
310
  throwIfCancelled(state);
293
- sendResponse(ws, message.id, true, result);
311
+ if (!sendResponse(ws, message.id, true, result, "", message.method)) {
312
+ closeSocketQuietly(ws, 1011, "browser response delivery failed");
313
+ }
294
314
  } catch (error) {
295
- if (!state.cancelled) sendResponse(ws, message.id, false, null, String(error?.message || error).slice(0, 2000));
315
+ if (!state.cancelled && !sendResponse(ws, message.id, false, null, globalThis.__machineBridgeBrowserErrorBoundary.publicError(error))) {
316
+ closeSocketQuietly(ws, 1011, "browser error delivery failed");
317
+ }
296
318
  } finally {
297
319
  activeRequests.delete(message.id);
298
320
  }
@@ -308,19 +330,17 @@ function throwIfCancelled(state) {
308
330
  if (state.cancelled) throw new Error("browser request cancelled");
309
331
  }
310
332
 
311
- function sendResponse(ws, id, ok, result, error = "") {
312
- if (ws.readyState !== WebSocket.OPEN) return;
313
- let payload = JSON.stringify({ type: "response", id, ok, ...(ok ? { result } : { error }) });
314
- if (new TextEncoder().encode(payload).byteLength > MAX_RESULT_BYTES) {
315
- payload = JSON.stringify({ type: "response", id, ok: false, error: "browser result exceeds maximum size" });
316
- }
317
- sendSocketQuietly(ws, payload);
333
+ function sendResponse(ws, id, ok, result, error = "", method = "") {
334
+ if (ws.readyState !== WebSocket.OPEN) return false;
335
+ const payload = browserOperations().responsePayload({ id, ok, result, error, method, maxBytes: MAX_RESULT_BYTES });
336
+ return sendSocketQuietly(ws, payload);
318
337
  }
319
338
 
320
-
321
339
  function browserOperations() {
322
340
  const api = globalThis.__machineBridgeBrowserOperations;
323
- if (!api || typeof api.dispatch !== "function") throw new Error("browser operations module is unavailable");
341
+ if (!api || typeof api.dispatch !== "function" || typeof api.methodMayMutate !== "function" || typeof api.responsePayload !== "function") {
342
+ throw new Error("browser operations module is unavailable");
343
+ }
324
344
  return api;
325
345
  }
326
346
 
@@ -8,13 +8,13 @@ This approximates a local coding agent without pretending that the MCP server ow
8
8
 
9
9
  - `session_bootstrap` returns built-in working agreements, bounded automatic project facts, explicit instruction text, and a refresh fingerprint.
10
10
  - `agent_context` returns the complete target-specific instruction chain, skill summaries, and command registry.
11
- - `resolve_task_capabilities` rescans default/project context and ranks skills/commands for the current task, optionally loading the best skill; the runtime also adds application and browser capability metadata.
11
+ - `resolve_task_capabilities` rescans default/project context, ranks skills/commands and effective-policy-visible tools, returns bounded set-level execution routes with ambiguity/fallback metadata, and optionally loads the best skill. Direct Bash remains available whenever the effective policy permits it.
12
12
  - `list_local_skills` searches discovered `SKILL.md` bundles.
13
13
  - `load_local_skill` returns one skill entrypoint and bounded file inventory without execution.
14
14
  - `list_local_commands` returns effective registered commands.
15
15
  - `run_local_command` executes a registered direct-argv command when policy permits.
16
16
 
17
- Both stdio and remote Worker connection initialization attempt `session_bootstrap`. Its instruction text is appended to the MCP `initialize` result. Because a host may reuse one MCP connection across conversations, the explicit tool and per-task `resolve_task_capabilities` call remain necessary to refresh and reapply instructions reliably. `server_info.observability.capability_routing` and `project_overview.capabilityRouting` report whether those calls reached the local runtime, their counts and timestamps, loaded-source flags, selected capability metadata, and a runtime-keyed HMAC task fingerprint. Raw task text is not retained.
17
+ MCP `2026-07-28` `server/discover` is intentionally static and does not call the daemon bootstrap path. Every executable request carries current protocol metadata, and clients use the explicit `session_bootstrap` or capability-resolution tools when refreshed local context is needed; `initialize` and protocol-session state are not served. Because a host may reuse one transport connection across conversations, call `resolve_task_capabilities` when the current task depends on refreshed project instructions, local skills/registered commands, or application/browser route selection. Straightforward file, Git, or shell work can use the already exposed tools directly without adding a resolver round trip. For shallow workspace identity/inventory without loading skills, commands, instruction bodies, or exact tool membership, use `project_overview` with `detail: "summary"`; reserve `agent_context` and `resolve_task_capabilities` for tasks that actually require those heavier context surfaces. A host may return the previous `refresh.fingerprint` as `known_refresh_fingerprint`; a match suppresses only unchanged static instruction metadata, not the fresh task-specific scan or routing when resolution is requested. Local stdio `server_info.observability.capability_routing` and both transports’ `project_overview.capabilityRouting` report whether those calls reached the local runtime, their counts and timestamps, loaded-source flags, selected capability metadata, primary route, ambiguity class, score gap, and a runtime-keyed HMAC task fingerprint. Raw task text is not retained.
18
18
 
19
19
  ## Useful defaults without configuration
20
20
 
@@ -106,11 +106,11 @@ chmod 600 ~/.config/machine-bridge-mcp/agent.json ~/.config/machine-bridge-mcp/M
106
106
 
107
107
  The file must be a non-empty, regular, non-symbolic-link UTF-8 file and is bounded by the hard instruction-file limit. Relative values are resolved from the user's home directory. Keep credentials, tokens, private keys, and unrelated personal data out of instruction files.
108
108
 
109
- Changes are discovered on the next `session_bootstrap`, `agent_context`, or `resolve_task_capabilities` call and do not require daemon restart. Because an MCP host may reuse a connection and may not call those tools automatically, start a new conversation or reconnect the MCP client when revised global instructions must be present in initialization context from the beginning.
109
+ Changes are discovered on the next `session_bootstrap`, `agent_context`, or `resolve_task_capabilities` call and do not require daemon restart. `server/discover` does not load local project instructions, so starting a new conversation or reconnecting is not a substitute for an explicit refresh call.
110
110
 
111
111
  `model_instructions_file`, `builtin_instructions`, and `automatic_project_context` are global-only. Project manifests cannot set or override them. The global model file and the two boolean controls are honored under workspace-confined profiles; other user-manifest fields that would widen filesystem/skill/command scope remain ignored there.
112
112
 
113
- In remote mode, the selected instruction text and enabled automatic project facts necessarily traverse the user's Cloudflare Worker and authorized MCP host as part of initialization. Do not put credentials or private records in instructions.
113
+ In remote mode, selected instruction text and enabled automatic project facts necessarily traverse the user's Cloudflare Worker and authorized MCP host when an explicit context/bootstrap call returns them. Do not put credentials or private records in instructions.
114
114
 
115
115
  ## Instruction precedence
116
116
 
@@ -190,15 +190,17 @@ description: Review a release without publishing it.
190
190
 
191
191
  The entrypoint requires non-empty `name` and `description`. Invalid bundles are skipped with bounded warnings. Symlinked skill directories are followed after canonical policy validation; symbolic-link entrypoint files are rejected. Traversal, depth, entries, summaries, content, and inventory are bounded.
192
192
 
193
- No persistent skill or project-context index is trusted as authoritative. `session_bootstrap`, `agent_context`, and `resolve_task_capabilities` rebuild the relevant context; skill-list/load calls rescan effective roots. The refresh fingerprint changes when built-in/default context, explicit instruction hashes, skill hashes, command definitions, or relevant configuration changes. Newly created or edited files are visible without restarting the daemon or changing the MCP tool catalog.
193
+ No persistent skill or project-context index is trusted as authoritative. `session_bootstrap`, `agent_context`, and `resolve_task_capabilities` rebuild the relevant context; skill-list/load calls rescan effective roots. The refresh fingerprint binds the target/scope, configuration paths, instruction source/precedence/content identity, skill source identity, and complete registered-command definition including cwd and timeout. A matching `known_refresh_fingerprint` permits response compaction only; task ranking, effective-policy filtering, installed-application matching, and route scoring still run. Newly created or edited files are visible without restarting the daemon or changing the MCP tool catalog.
194
194
 
195
195
  ## Progressive disclosure and task selection
196
196
 
197
- `agent_context` returns bounded skill metadata. `load_local_skill` returns full instructions only for one selected bundle. `resolve_task_capabilities` tokenizes the current task, ranks skill names/descriptions and command names/descriptions/argv, returns matches with scores, and loads the leading skill only when its relevance threshold is met. Under canonical `full`, it also compares the task with installed application names on every call; application discovery is cached briefly and refreshed after a bounded interval.
197
+ `agent_context` returns bounded skill metadata. `load_local_skill` returns full instructions only for one selected bundle. `resolve_task_capabilities` tokenizes the current task, ranks skill names/descriptions, command names/descriptions/argv, and public tool definitions, then scores compatible execution surfaces as sets. It returns a schema-versioned envelope with the primary route, alternatives, ambiguity, fallback routes, ranked tools, and failure-aware guidance. Scores are relative within the current response, not probabilities or values to compare across package versions. Registered commands, direct Bash/argv, interactive sessions, durable jobs, files/Git, browser, applications, protected resources, and diagnostics remain separate choices; the advice does not hide or disable any effective-policy-visible tool.
198
+
199
+ The resolver compares the task with cached installed-application names for every authenticated policy profile. This is an intentional read-only inventory surface: delegated roles may see application names and launcher/bundle identifiers, while external paths remain projected. Action routes are still filtered through the authenticated account/daemon policy intersection, so a delegated reviewer connected to a full daemon can discover an installed application without acquiring launch, Accessibility, browser, shell, or write authority.
198
200
 
199
201
  Matching remains deterministic and local. Hyphens, underscores, dots, and whitespace are normalized; common English inflections are reduced to a small canonical form; and a bounded Chinese/English workflow vocabulary covers creation, improvement, installation, search, current/official documentation, verification, testing, frontend/design, browser/web, email, performance, and security intents. Capability-name token matches receive more weight than incidental words in a long description. This lets Chinese tasks select English-metadata skills such as `skill-creator`, `web-research-cli`, and `skill-installer`, while avoiding the prior tie where generic “create” wording could select `frontend-design`. An explicitly named skill or registered command still receives the strongest deterministic boost.
200
202
 
201
- This ranking is deterministic local assistance, not semantic certainty. Weak positive matches remain visible for diagnosis, but only a skill meeting the selection threshold is recommended for loading. The model must still evaluate whether the selected skill applies. Machine Bridge does not execute skill scripts implicitly and does not fabricate a dynamically named MCP tool per skill.
203
+ This ranking is deterministic local assistance, not semantic certainty. Weak positive matches remain visible for diagnosis, but only a skill meeting the selection threshold is recommended for loading. A high-ambiguity result tells the host to compare the competing route sets rather than over-trust one name. Fallbacks are recovery alternatives, not permission to evade a host or policy denial. The model must still evaluate whether the selected skill and route apply. Machine Bridge does not execute skill scripts implicitly and does not fabricate a dynamically named MCP tool per skill.
202
204
 
203
205
  ## Registered and automatic package commands
204
206
 
@@ -213,12 +215,13 @@ Directly invoking `npm run`, `pnpm run`, `yarn run`, or `bun run` does not make
213
215
  ## Recommended host workflow
214
216
 
215
217
  1. consume MCP initialization instructions, including the built-in baseline and automatic project facts;
216
- 2. call `resolve_task_capabilities` with the complete user task and target path;
218
+ 2. when the task needs refreshed instructions, local skill/command discovery, or application/browser route selection, call `resolve_task_capabilities` with the complete user task and target path; otherwise use the already exposed direct tool;
217
219
  3. apply explicit global/project instructions over lower-precedence defaults;
218
220
  4. follow the selected skill only after checking relevance;
219
- 5. prefer registered commands for stable workflows;
221
+ 5. use the returned route set as advice: prefer registered commands for stable workflows, direct Bash for efficient ad hoc composition, process sessions for interactive work, and managed jobs for disconnect-surviving multi-step work;
220
222
  6. use structured application/browser tools where applicable;
221
- 7. inspect before mutation or submission and report operations performed.
223
+ 7. inspect stable state after ambiguous mutation failures before retrying;
224
+ 8. inspect before mutation or submission and report operations performed.
222
225
 
223
226
  Machine Bridge can automatically discover, refresh, rank, and load capabilities. Actual invocation remains a host/model decision. This boundary cannot be removed by server architecture alone.
224
227