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,266 @@
1
+ import { createHash } from "node:crypto";
2
+ import { clampInt, normalizeBrowserSnapshotIdentity, normalizeImageFormat, optionalBoolean, optionalInteger, optionalString } from "./browser-command.mjs";
3
+
4
+ const MAX_BROWSER_SCREENSHOT_BYTES = 32 * 1024 * 1024;
5
+
6
+ export function normalizeBackendNodeActionMetadata(args = {}) {
7
+ const action = args.action;
8
+ if (typeof action !== "string" || !["click", "double_click", "hover", "drag", "scroll", "press", "type_text", "fill", "check", "uncheck", "submit"].includes(action)) {
9
+ throw new Error("snapshot backend action must be click, double_click, hover, drag, scroll, press, type_text, fill, check, uncheck, or submit");
10
+ }
11
+ const backendNodeId = requiredPositiveInteger(args.backend_node_id, "backend_node_id");
12
+ const destinationBackendNodeId = action === "drag"
13
+ ? requiredPositiveInteger(args.destination_backend_node_id, "destination_backend_node_id")
14
+ : null;
15
+ const scrollDeltas = action === "scroll" ? normalizeScrollDeltas(args) : null;
16
+ const timeoutSeconds = clampInt(args.timeout_seconds, 30, 1, 120);
17
+ const payload = {
18
+ tabId: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
19
+ action,
20
+ backendNodeId,
21
+ extensionFrameId: optionalAuthorityInteger(args.extension_frame_id, "extension_frame_id"),
22
+ expectedDocumentEpoch: optionalEpoch(args.document_epoch),
23
+ expectedFrameDocumentEpoch: optionalEpoch(args.frame_document_epoch),
24
+ expectedFrameUrl: optionalFrameUrl(args.frame_url),
25
+ extensionRef: optionalString(args.extension_ref, "extension_ref", 100),
26
+ expectedIdentity: normalizeBrowserSnapshotIdentity(args.expected_ref_identity),
27
+ ...(action === "drag" ? {
28
+ destinationBackendNodeId,
29
+ destinationExtensionFrameId: optionalAuthorityInteger(args.destination_extension_frame_id, "destination_extension_frame_id"),
30
+ destinationExpectedFrameDocumentEpoch: optionalEpoch(args.destination_frame_document_epoch),
31
+ destinationExpectedFrameUrl: optionalFrameUrl(args.destination_frame_url),
32
+ destinationExtensionRef: optionalString(args.destination_extension_ref, "destination_extension_ref", 100),
33
+ destinationExpectedIdentity: normalizeBrowserSnapshotIdentity(args.destination_expected_ref_identity),
34
+ } : {}),
35
+ ...(scrollDeltas || {}),
36
+ value: args.value === undefined ? undefined : requiredText(args.value, "value", 131072),
37
+ key: args.key === undefined ? undefined : requiredText(args.key, "key", 100),
38
+ };
39
+ return { payload, timeoutSeconds };
40
+ }
41
+
42
+ export class BrowserComputerObservationService {
43
+ constructor({ authorizeTool, request, bridgeStatus, inspectPage, screenshot }) {
44
+ this.authorizeTool = authorizeTool;
45
+ this.request = request;
46
+ this.bridgeStatus = bridgeStatus;
47
+ this.inspectPage = inspectPage;
48
+ this.screenshot = screenshot;
49
+ }
50
+
51
+ async documentState(args = {}, context = {}) {
52
+ this.authorizeTool("computer_act");
53
+ return this.request("document_state", {
54
+ tabId: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
55
+ }, clampInt(args.timeout_seconds, 5, 1, 30), context);
56
+ }
57
+
58
+ preflightBackendNodeAction(args = {}) {
59
+ this.authorizeTool("computer_act");
60
+ return normalizeBackendNodeActionMetadata(args);
61
+ }
62
+
63
+ async backendNodeAction(args = {}, context = {}) {
64
+ this.authorizeTool("computer_act");
65
+ const prepared = normalizeBackendNodeActionMetadata(args);
66
+ return this.request("backend_node_action", prepared.payload, prepared.timeoutSeconds, context);
67
+ }
68
+
69
+ async pointAction(args = {}, context = {}) {
70
+ this.authorizeTool("computer_act");
71
+ const action = args.action;
72
+ if (typeof action !== "string" || !["click", "double_click", "hover", "drag", "scroll"].includes(action)) throw new Error("visual point action must be click, double_click, hover, drag, or scroll");
73
+ const scrollDeltas = action === "scroll" ? normalizeScrollDeltas(args) : null;
74
+ return this.request("point_action", {
75
+ tabId: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
76
+ action,
77
+ normalizedX: normalizedCoordinate(args.normalized_x, "normalized_x"),
78
+ normalizedY: normalizedCoordinate(args.normalized_y, "normalized_y"),
79
+ ...(action === "drag" ? {
80
+ destinationNormalizedX: normalizedCoordinate(args.destination_normalized_x, "destination_normalized_x"),
81
+ destinationNormalizedY: normalizedCoordinate(args.destination_normalized_y, "destination_normalized_y"),
82
+ } : {}),
83
+ ...(scrollDeltas || {}),
84
+ expectedDocumentEpoch: optionalEpoch(args.document_epoch),
85
+ expectedViewport: normalizeExpectedViewport(args.viewport),
86
+ expectedScreenshotSha256: optionalSha256(args.screenshot_sha256),
87
+ screenshotFormat: normalizeImageFormat(args.screenshot_format, "screenshot_format"),
88
+ screenshotQuality: clampInt(args.screenshot_quality, 90, 1, 100),
89
+ }, clampInt(args.timeout_seconds, 30, 1, 120), context);
90
+ }
91
+
92
+ async observe(args = {}, context = {}) {
93
+ this.authorizeTool("computer_observe");
94
+ const timeoutSeconds = clampInt(args.timeout_seconds, 30, 1, 120);
95
+ const normalized = {
96
+ tab_id: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
97
+ all_frames: optionalBoolean(args.all_frames, "all_frames", true),
98
+ max_elements: clampInt(args.max_elements, 300, 1, 1000),
99
+ max_ax_nodes: clampInt(args.max_ax_nodes, 600, 1, 2000),
100
+ max_frames: clampInt(args.max_frames, 32, 1, 64),
101
+ ax_depth: clampInt(args.ax_depth, 12, 1, 16),
102
+ include_values: optionalBoolean(args.include_values, "include_values", false),
103
+ include_screenshot: optionalBoolean(args.include_screenshot, "include_screenshot", true),
104
+ screenshot_format: normalizeImageFormat(args.screenshot_format, "screenshot_format"),
105
+ screenshot_quality: clampInt(args.screenshot_quality, 90, 1, 100),
106
+ focus_query: optionalFocusQuery(args.focus_query),
107
+ };
108
+ const capabilities = new Set(this.bridgeStatus().extensionInfo?.capabilities || []);
109
+ if (!capabilities.has("computer_observation_v1")) return this.observeLegacy(normalized, timeoutSeconds, context);
110
+
111
+ const result = await this.request("observe_computer", {
112
+ tabId: normalized.tab_id, allFrames: normalized.all_frames, maxElements: normalized.max_elements, maxAxNodes: normalized.max_ax_nodes,
113
+ maxFrames: normalized.max_frames, axDepth: normalized.ax_depth, includeValues: normalized.include_values, includeScreenshot: normalized.include_screenshot,
114
+ format: normalized.screenshot_format, quality: normalized.screenshot_quality, focusQuery: normalized.focus_query,
115
+ }, timeoutSeconds, context);
116
+ const imageContent = computerObservationImage(result);
117
+ return {
118
+ ...result,
119
+ capture: { ...(result.capture || {}), screenshot_sha256: screenshotSha256(imageContent) },
120
+ imageContent,
121
+ };
122
+ }
123
+
124
+ async observeLegacy(args, timeoutSeconds, context) {
125
+ const inspected = await this.inspectPage({
126
+ tab_id: args.tab_id,
127
+ max_elements: args.max_elements,
128
+ include_values: args.include_values,
129
+ all_frames: args.all_frames,
130
+ timeout_seconds: timeoutSeconds,
131
+ focus_query: args.focus_query,
132
+ }, context);
133
+ let imageContent = [];
134
+ if (args.include_screenshot) {
135
+ const screenshot = await this.screenshot({
136
+ tab_id: inspected.tab_id,
137
+ format: args.screenshot_format,
138
+ quality: args.screenshot_quality,
139
+ timeout_seconds: timeoutSeconds,
140
+ }, context);
141
+ imageContent = Array.isArray(screenshot?.$mcp?.content) ? screenshot.$mcp.content : [];
142
+ }
143
+ const rawSemanticEpoch = inspected.frames?.find?.((frame) => frame.frame_id === 0)?.document?.epoch;
144
+ const semanticEpoch = typeof rawSemanticEpoch === "string" ? rawSemanticEpoch : "";
145
+ return {
146
+ tab_id: inspected.tab_id,
147
+ title: inspected.title || "",
148
+ url: inspected.url || "",
149
+ semantic: inspected,
150
+ accessibility: null,
151
+ viewport: null,
152
+ frame_tree: [],
153
+ document_epoch: semanticEpoch,
154
+ capture: {
155
+ atomic: false,
156
+ navigation_coherent: false,
157
+ semantic_epoch: semanticEpoch,
158
+ cdp_epoch: "",
159
+ cdp: false,
160
+ screenshot_source: imageContent.length ? "capture_visible_tab_legacy" : "none",
161
+ coherence: "legacy_extension_without_computer_observation_v1",
162
+ fallback_reason: "extension_reload_required_for_cdp_observation",
163
+ screenshot_sha256: screenshotSha256(imageContent),
164
+ },
165
+ imageContent,
166
+ };
167
+ }
168
+ }
169
+
170
+ function computerObservationImage(result) {
171
+ if (!result?.screenshot?.data) return [];
172
+ if (typeof result.screenshot.data !== "string") throw new Error("browser extension returned an invalid computer observation screenshot");
173
+ const match = /^data:(image\/(?:png|jpeg));base64,([A-Za-z0-9+/=]+)$/.exec(result.screenshot.data);
174
+ if (!match || !browserScreenshotBytes(match[2], match[1])) throw new Error("browser extension returned an invalid computer observation screenshot");
175
+ return [{ type: "image", data: match[2], mimeType: match[1] }];
176
+ }
177
+
178
+ function screenshotSha256(imageContent) {
179
+ const image = (imageContent || []).find((item) => item?.type === "image" && typeof item.data === "string");
180
+ const bytes = image ? browserScreenshotBytes(image.data, image.mimeType) : null;
181
+ return bytes ? createHash("sha256").update(bytes).digest("hex") : "";
182
+ }
183
+
184
+ function browserScreenshotBytes(value, mimeType) {
185
+ if (typeof value !== "string" || !value || value.length > Math.ceil(MAX_BROWSER_SCREENSHOT_BYTES / 3) * 4) return null;
186
+ const bytes = Buffer.from(value, "base64");
187
+ if (bytes.length < 3 || bytes.length > MAX_BROWSER_SCREENSHOT_BYTES || bytes.toString("base64") !== value) return null;
188
+ if (mimeType === "image/png") {
189
+ return bytes.length >= 8 && bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47
190
+ && bytes[4] === 0x0d && bytes[5] === 0x0a && bytes[6] === 0x1a && bytes[7] === 0x0a ? bytes : null;
191
+ }
192
+ return mimeType === "image/jpeg" && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff ? bytes : null;
193
+ }
194
+
195
+ function optionalFocusQuery(value) {
196
+ if (value === undefined) return "";
197
+ if (typeof value !== "string" || value.includes("\0") || value.length > 1000) throw new Error("focus_query must be a string of at most 1000 characters without NUL bytes");
198
+ return value.trim();
199
+ }
200
+
201
+ function normalizedCoordinate(value, label) {
202
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value >= 1) throw new Error(`${label} must be a number from 0 (inclusive) to 1 (exclusive)`);
203
+ return Object.is(value, -0) ? 0 : value;
204
+ }
205
+
206
+ function normalizeScrollDeltas(args) {
207
+ const deltaX = scrollDelta(args.delta_x, "delta_x");
208
+ const deltaY = scrollDelta(args.delta_y, "delta_y");
209
+ if (deltaX === 0 && deltaY === 0) throw new Error("scroll requires a non-zero delta_x or delta_y");
210
+ return { deltaX, deltaY };
211
+ }
212
+
213
+ function scrollDelta(value, label) {
214
+ if (value === undefined) return 0;
215
+ if (typeof value !== "number" || !Number.isFinite(value) || Math.abs(value) > 10_000) {
216
+ throw new Error(`${label} must be a finite number from -10000 to 10000`);
217
+ }
218
+ return Object.is(value, -0) ? 0 : value;
219
+ }
220
+
221
+ function requiredText(value, label, maxLength) {
222
+ if (typeof value !== "string" || value.includes("\0") || value.length > maxLength) {
223
+ throw new Error(`${label} must be a string of at most ${maxLength} characters without NUL bytes`);
224
+ }
225
+ return value;
226
+ }
227
+
228
+ function requiredPositiveInteger(value, label) {
229
+ if (!Number.isSafeInteger(value) || value < 1) throw new Error(`${label} must be a positive integer`);
230
+ return value;
231
+ }
232
+
233
+ function optionalAuthorityInteger(value, label) {
234
+ if (value === undefined) return null;
235
+ if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${label} must be a non-negative integer`);
236
+ return value;
237
+ }
238
+
239
+ function optionalEpoch(value) {
240
+ if (value === undefined) return "";
241
+ if (typeof value !== "string" || value.includes("\0") || value.length > 9000) throw new Error("document_epoch is invalid");
242
+ return value;
243
+ }
244
+
245
+ function optionalFrameUrl(value) {
246
+ if (value === undefined) return "";
247
+ if (typeof value !== "string" || value.includes("\0") || value.length > 8192) throw new Error("frame_url is invalid");
248
+ return value;
249
+ }
250
+
251
+ function optionalSha256(value) {
252
+ if (typeof value !== "string" || !/^[A-Fa-f0-9]{64}$/.test(value)) throw new Error("screenshot_sha256 must be a SHA-256 hex digest");
253
+ return value.toLowerCase();
254
+ }
255
+
256
+ function normalizeExpectedViewport(value) {
257
+ if (value === undefined) return null;
258
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("viewport must be an object");
259
+ const output = {};
260
+ for (const key of ["width", "height", "scale"]) {
261
+ const number = value[key];
262
+ if (typeof number !== "number" || !Number.isFinite(number) || number <= 0) throw new Error(`viewport.${key} must be a positive number`);
263
+ output[key] = number;
264
+ }
265
+ return output;
266
+ }
@@ -0,0 +1,50 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { packageRoot } from "./package-identity.mjs";
5
+
6
+ const EXTENSION_ID_PATTERN = /^[a-p]{32}$/;
7
+ const PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
8
+
9
+ const manifest = readExtensionManifest();
10
+ export const EXPECTED_EXTENSION_PUBLIC_KEY = normalizeManifestPublicKey(manifest.key);
11
+ export const EXPECTED_EXTENSION_ID = extensionIdFromPublicKey(EXPECTED_EXTENSION_PUBLIC_KEY);
12
+
13
+ export function extensionIdFromPublicKey(value) {
14
+ const publicKey = normalizeManifestPublicKey(value);
15
+ const bytes = Buffer.from(publicKey, "base64");
16
+ const hexadecimal = createHash("sha256").update(bytes).digest("hex").slice(0, 32);
17
+ const extensionId = [...hexadecimal]
18
+ .map((character) => String.fromCharCode(97 + Number.parseInt(character, 16)))
19
+ .join("");
20
+ if (!EXTENSION_ID_PATTERN.test(extensionId)) throw new Error("browser extension public key produced an invalid extension id");
21
+ return extensionId;
22
+ }
23
+
24
+ export function normalizeExtensionId(value) {
25
+ const extensionId = String(value || "").trim().toLowerCase();
26
+ return EXTENSION_ID_PATTERN.test(extensionId) ? extensionId : "";
27
+ }
28
+
29
+ function normalizeManifestPublicKey(value) {
30
+ const publicKey = String(value || "").trim();
31
+ if (!PUBLIC_KEY_PATTERN.test(publicKey) || publicKey.length < 128 || publicKey.length > 8192) {
32
+ throw new Error("browser extension manifest must contain a bounded base64 public key");
33
+ }
34
+ let bytes;
35
+ try { bytes = Buffer.from(publicKey, "base64"); }
36
+ catch { throw new Error("browser extension manifest public key is invalid base64"); }
37
+ if (bytes.length < 96 || bytes.length > 6144 || bytes.toString("base64") !== publicKey) {
38
+ throw new Error("browser extension manifest public key is not canonical DER base64");
39
+ }
40
+ return publicKey;
41
+ }
42
+
43
+ function readExtensionManifest() {
44
+ const path = resolve(packageRoot, "browser-extension", "manifest.json");
45
+ const manifestValue = JSON.parse(readFileSync(path, "utf8"));
46
+ if (!manifestValue || typeof manifestValue !== "object" || Array.isArray(manifestValue)) {
47
+ throw new Error("browser extension manifest is invalid");
48
+ }
49
+ return manifestValue;
50
+ }
@@ -0,0 +1,22 @@
1
+ // @ts-check
2
+ import { isAbsolute, relative, resolve, sep } from "node:path";
3
+ import { packageRoot } from "./package-identity.mjs";
4
+ import { isCandidateRuntimeDirectoryName } from "./state-root-owned-namespaces.mjs";
5
+
6
+ export const RELEASE_BROWSER_EXTENSION_DIRECTORY = "browser-extension";
7
+
8
+ export function releaseBrowserExtensionPath(stateRoot) {
9
+ return resolve(String(stateRoot || ""), "release-channels", RELEASE_BROWSER_EXTENSION_DIRECTORY);
10
+ }
11
+
12
+ export function browserExtensionPathForRuntime({ stateRoot = "", packageDirectory = packageRoot } = {}) {
13
+ const packaged = resolve(packageDirectory, "browser-extension");
14
+ if (!stateRoot) return packaged;
15
+ const runtimes = resolve(stateRoot, "release-channels", "runtimes");
16
+ const runtimeRelative = relative(runtimes, resolve(packageDirectory));
17
+ if (!runtimeRelative || runtimeRelative === ".." || runtimeRelative.startsWith(`..${sep}`) || isAbsolute(runtimeRelative)) {
18
+ return packaged;
19
+ }
20
+ const [runtimeName] = runtimeRelative.split(sep);
21
+ return isCandidateRuntimeDirectoryName(runtimeName) ? releaseBrowserExtensionPath(stateRoot) : packaged;
22
+ }
@@ -1,8 +1,8 @@
1
1
  // @ts-check
2
-
3
2
  import { readFileSync } from "node:fs";
4
3
  import { resolve } from "node:path";
5
- import { packageRoot } from "./state.mjs";
4
+ import { packageRoot } from "./package-identity.mjs";
5
+ import { EXPECTED_EXTENSION_ID, normalizeExtensionId } from "./browser-extension-identity.mjs";
6
6
 
7
7
  export const BROWSER_EXTENSION_PROTOCOL = 3;
8
8
  export const EXPECTED_EXTENSION_VERSION = extensionVersion();
@@ -10,14 +10,28 @@ const REQUIRED_EXTENSION_CAPABILITIES = Object.freeze([
10
10
  "semantic_snapshot_refs", "actionability_waits", "trusted_input", "tab_management", "explicit_waits",
11
11
  ]);
12
12
  export const MAX_BROWSER_MESSAGE_BYTES = 8 * 1024 * 1024;
13
+ const MUTATING_BROWSER_METHODS = new Set([
14
+ "manage_tabs", "point_action", "backend_node_action", "action", "fill_form", "upload_files", "screenshot",
15
+ ]);
16
+ const BROWSER_MUTATION_TRANSPORT_UNKNOWN = "browser mutation request may have been dispatched; the outcome is unknown because its response was not observed. Inspect browser state before retrying.";
17
+
18
+ /** @param {unknown} method */
19
+ export function browserMethodMayMutate(method) {
20
+ return typeof method === "string" && MUTATING_BROWSER_METHODS.has(method);
21
+ }
13
22
 
14
- /** @typedef {{protocol: number, version: string, capabilities: string[]}} ExtensionInfo */
23
+ /** @param {unknown} method @param {unknown} fallback */
24
+ export function browserPostDispatchTransportFailure(method, fallback) {
25
+ if (browserMethodMayMutate(method)) return BROWSER_MUTATION_TRANSPORT_UNKNOWN;
26
+ return typeof fallback === "string" && fallback ? fallback.slice(0, 2000) : "browser transport failed";
27
+ }
28
+ /** @typedef {{protocol: number, version: string, extension_id: string, capabilities: string[]}} ExtensionInfo */
15
29
  /** @typedef {{readyState: number, close: (code?: number, reason?: string) => unknown, send: (value: string) => unknown}} ProtocolSocket */
16
30
 
17
31
  /** @param {unknown} value @returns {ExtensionInfo | null} */
18
32
  export function normalizeCompatibleExtensionInfo(value) {
19
33
  const info = normalizeExtensionInfo(value);
20
- if (!info || info.protocol !== BROWSER_EXTENSION_PROTOCOL || info.version !== EXPECTED_EXTENSION_VERSION) return null;
34
+ if (!info || info.protocol !== BROWSER_EXTENSION_PROTOCOL || info.version !== EXPECTED_EXTENSION_VERSION || info.extension_id !== EXPECTED_EXTENSION_ID) return null;
21
35
  if (REQUIRED_EXTENSION_CAPABILITIES.some((capability) => !info.capabilities.includes(capability))) return null;
22
36
  return info;
23
37
  }
@@ -32,11 +46,13 @@ export function parseExtensionHello(message) {
32
46
  if (info.version !== EXPECTED_EXTENSION_VERSION) {
33
47
  throw new Error(`extension version mismatch; expected ${EXPECTED_EXTENSION_VERSION}; reload the extension`);
34
48
  }
49
+ if (info.extension_id !== EXPECTED_EXTENSION_ID) {
50
+ throw new Error(`extension identity mismatch; expected ${EXPECTED_EXTENSION_ID}; reload the packaged extension`);
51
+ }
35
52
  const missing = REQUIRED_EXTENSION_CAPABILITIES.filter((capability) => !info.capabilities.includes(capability));
36
53
  if (missing.length) throw new Error(`extension capability mismatch; reload the extension (${missing.join(",")})`);
37
54
  return info;
38
55
  }
39
-
40
56
  /**
41
57
  * @param {string | Buffer | Uint8Array} data
42
58
  * @returns {{ok: true, message: Record<string, unknown>} | {ok: false, code: number, reason: string}}
@@ -54,10 +70,28 @@ export function parseBrowserSocketMessage(data) {
54
70
  }
55
71
  return { ok: true, message };
56
72
  }
57
-
73
+ /** @param {unknown} value */
74
+ export function normalizeRuntimeBrowserRequest(value) {
75
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
76
+ const message = /** @type {Record<string, unknown>} */ (value);
77
+ const id = boundedProtocolString(message.id, 200);
78
+ const method = typeof message.method === "string" && /^[a-z][a-z0-9_]{0,63}$/.test(message.method) ? message.method : "";
79
+ const params = message.params === undefined ? {} : message.params;
80
+ const timeoutMs = message.timeout_ms === undefined ? 30_000 : message.timeout_ms;
81
+ const callId = message.call_id === undefined ? "" : boundedProtocolString(message.call_id, 200);
82
+ if (message.type !== "request" || !id || !method || !params || typeof params !== "object" || Array.isArray(params)
83
+ || !Number.isSafeInteger(timeoutMs)
84
+ || (message.call_id !== undefined && !callId)) return null;
85
+ return { type: "request", id, method, params, timeout_ms: timeoutMs, ...(callId ? { call_id: callId } : {}) };
86
+ }
87
+ /** @param {unknown} value @param {number} maximum */
88
+ function boundedProtocolString(value, maximum) {
89
+ return typeof value === "string" && value && value.length <= maximum && !value.includes("\0") ? value : "";
90
+ }
58
91
  /** @param {ProtocolSocket} socket @param {number} code @param {string} reason */
59
92
  export function closeProtocolSocket(socket, code, reason) {
60
- try { socket.close(code, reason); } catch {}
93
+ try { socket.close(code, reason); }
94
+ catch { /* Protocol rejection is already decided; a concurrently closed socket needs no second failure path. */ }
61
95
  }
62
96
 
63
97
  /** @param {ProtocolSocket | null | undefined} socket @param {unknown} value */
@@ -75,16 +109,21 @@ export function safeSocketSend(socket, value) {
75
109
  function normalizeExtensionInfo(value) {
76
110
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
77
111
  const record = /** @type {Record<string, unknown>} */ (value);
78
- const protocol = Number(record.protocol);
112
+ const protocol = record.protocol;
79
113
  const version = typeof record.version === "string" && record.version.length <= 100 ? record.version : "";
114
+ const extension_id = normalizeExtensionId(record.extension_id);
80
115
  const capabilities = Array.isArray(record.capabilities)
81
116
  ? [...new Set(record.capabilities.filter((entry) => typeof entry === "string" && /^[a-z][a-z0-9_]{0,63}$/.test(entry)))].slice(0, 32)
82
117
  : [];
83
- if (!Number.isInteger(protocol) || protocol < 1 || !version) return null;
84
- return { protocol, version, capabilities };
118
+ if (typeof protocol !== "number" || !Number.isInteger(protocol) || protocol < 1 || !version || !extension_id) return null;
119
+ return { protocol, version, extension_id, capabilities };
85
120
  }
86
121
 
87
122
  function extensionVersion() {
88
123
  const manifest = JSON.parse(readFileSync(resolve(packageRoot, "browser-extension", "manifest.json"), "utf8"));
89
- return String(manifest.version_name || manifest.version || "");
124
+ const version = manifest.version_name === undefined ? manifest.version : manifest.version_name;
125
+ if (typeof version !== "string" || !version || version.length > 100 || version.includes("\0")) {
126
+ throw new Error("browser extension manifest version is invalid");
127
+ }
128
+ return version;
90
129
  }