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,119 @@
1
+ import {
2
+ pruneClientRecordByExpiry,
3
+ pruneRecordByExpiry,
4
+ randomToken,
5
+ sha256Hex,
6
+ type OAuthCode,
7
+ type OAuthRefreshStore,
8
+ type OAuthRefreshToken,
9
+ type OAuthStore,
10
+ } from "./oauth-state.ts";
11
+ import { HttpError, json, workerErrorClass } from "./http.ts";
12
+ import { writeOAuthRefreshPersistenceEntries } from "./oauth-refresh-persistence.ts";
13
+ import { deriveRefreshReplacementPair } from "./oauth-token-derivation.ts";
14
+ import type { AuthorityRevocation } from "../shared/authority-revocation.mjs";
15
+
16
+ const ACCESS_TOKEN_TTL_SECONDS = 15 * 60;
17
+ const REFRESH_TOKEN_IDLE_TTL_SECONDS = 60 * 60 * 24 * 14;
18
+ const REFRESH_TOKEN_FAMILY_TTL_SECONDS = 60 * 60 * 24 * 30;
19
+ const MAX_ACCESS_TOKENS = 500;
20
+ const MAX_ACCESS_TOKENS_PER_CLIENT = 20;
21
+ const MAX_REFRESH_TOKENS = 500;
22
+ const MAX_REFRESH_TOKENS_PER_CLIENT = 20;
23
+
24
+ type OAuthLock = <T>(callback: () => Promise<T>) => Promise<T>;
25
+ export type OAuthRefreshEvent = "rotated" | "retry_issued" | "retry_exhausted" | "family_revoked" | "rejected";
26
+
27
+ export interface OAuthTokenExchangeOptions {
28
+ storage: DurableObjectStorage;
29
+ tokenVersion: string;
30
+ serverName: string;
31
+ loadOAuthStore: () => Promise<OAuthStore>;
32
+ withLock: OAuthLock;
33
+ onRefreshEvent?: (event: OAuthRefreshEvent) => void;
34
+ saveStores: (oauthStore: OAuthStore, refreshStore: OAuthRefreshStore, revocation?: AuthorityRevocation) => Promise<void>;
35
+ }
36
+
37
+ export interface IssuedTokenPair {
38
+ accessToken: string;
39
+ refreshToken: string;
40
+ }
41
+
42
+ export async function issueTokenPair(
43
+ oauthStore: OAuthStore,
44
+ refreshStore: OAuthRefreshStore,
45
+ source: OAuthCode | OAuthRefreshToken,
46
+ tokenVersion: string,
47
+ dpopJkt?: string,
48
+ issuance: { derivationSeed?: string; issuedAt?: number; accessScope?: string } = {},
49
+ ): Promise<IssuedTokenPair> {
50
+ if (!tokenVersion) throw new HttpError(503, "server_error", "OAuth token version is not configured");
51
+ const now = Number.isSafeInteger(issuance.issuedAt) && Number(issuance.issuedAt) > 0
52
+ ? Number(issuance.issuedAt)
53
+ : Math.floor(Date.now() / 1000);
54
+ const familyId = "family_id" in source && source.family_id ? source.family_id : randomToken("mcp_family");
55
+ const familyExpiresAt = "family_expires_at" in source && source.family_expires_at
56
+ ? source.family_expires_at
57
+ : now + REFRESH_TOKEN_FAMILY_TTL_SECONDS;
58
+ if (familyExpiresAt <= now) throw new HttpError(400, "invalid_grant", "refresh-token family expired");
59
+ const issued = issuance.derivationSeed
60
+ ? await deriveRefreshReplacementPair(tokenVersion, issuance.derivationSeed)
61
+ : { accessToken: randomToken("mcp_at"), refreshToken: randomToken("mcp_rt") };
62
+ const { accessToken, refreshToken } = issued;
63
+ const accessScope = typeof issuance.accessScope === "string" && issuance.accessScope.trim()
64
+ ? issuance.accessScope.trim()
65
+ : source.scope;
66
+ const common = {
67
+ client_id: source.client_id,
68
+ account_id: source.account_id,
69
+ account_version: source.account_version,
70
+ role: source.role,
71
+ resource: source.resource,
72
+ version: tokenVersion,
73
+ family_id: familyId,
74
+ ...(dpopJkt ? { dpop_jkt: dpopJkt } : {}),
75
+ };
76
+ oauthStore.tokens[`sha256:${await sha256Hex(accessToken)}`] = {
77
+ ...common,
78
+ scope: accessScope,
79
+ expires_at: now + ACCESS_TOKEN_TTL_SECONDS,
80
+ };
81
+ refreshStore.tokens[`sha256:${await sha256Hex(refreshToken)}`] = {
82
+ ...common,
83
+ scope: source.scope,
84
+ family_id: familyId,
85
+ family_expires_at: familyExpiresAt,
86
+ issued_at: now,
87
+ expires_at: Math.min(now + REFRESH_TOKEN_IDLE_TTL_SECONDS, familyExpiresAt),
88
+ };
89
+ pruneClientRecordByExpiry(oauthStore.tokens, source.client_id, MAX_ACCESS_TOKENS_PER_CLIENT);
90
+ pruneRecordByExpiry(oauthStore.tokens, MAX_ACCESS_TOKENS);
91
+ pruneClientRecordByExpiry(refreshStore.tokens, source.client_id, MAX_REFRESH_TOKENS_PER_CLIENT);
92
+ pruneRecordByExpiry(refreshStore.tokens, MAX_REFRESH_TOKENS);
93
+ return { accessToken, refreshToken };
94
+ }
95
+
96
+ export function tokenResponse(issued: IssuedTokenPair, scope: string, dpopJkt?: string): Response {
97
+ return json({
98
+ access_token: issued.accessToken,
99
+ refresh_token: issued.refreshToken,
100
+ token_type: dpopJkt ? "DPoP" : "Bearer",
101
+ expires_in: ACCESS_TOKEN_TTL_SECONDS,
102
+ scope,
103
+ }, 200, { pragma: "no-cache" });
104
+ }
105
+
106
+ export async function saveOAuthStores(
107
+ oauthStore: OAuthStore, refreshStore: OAuthRefreshStore, storage: DurableObjectStorage,
108
+ ): Promise<void> {
109
+ let stage = "oauth";
110
+ try {
111
+ await storage.transaction(async (tx) => {
112
+ await tx.put("oauth", oauthStore); stage = "refresh";
113
+ await writeOAuthRefreshPersistenceEntries(tx, refreshStore); stage = "commit";
114
+ });
115
+ } catch (error) {
116
+ const wrapped = new Error("OAuth store persistence failed", { cause: error });
117
+ wrapped.name = `oauth_store_persist_${stage}_${workerErrorClass(error)}`; throw wrapped;
118
+ }
119
+ }
@@ -1,34 +1,18 @@
1
- import {
2
- normalizeOAuthScope, pkceS256, pruneClientRecordByExpiry, pruneRecordByExpiry,
3
- randomToken, safeEqual, sha256Hex,
4
- type OAuthCode, type OAuthRefreshStore, type OAuthRefreshToken, type OAuthStore,
5
- } from "./oauth-state.ts";
1
+ import { consumeDpopProof, verifyDpopProof, type VerifiedDpopProof } from "./dpop.ts";
6
2
  import { HttpError, json, parseRequestBody } from "./http.ts";
7
- import { loadOAuthRefreshStore, OAUTH_REFRESH_STORE_KEY, recordConsumedRefreshToken, revokeOAuthRefreshFamily } from "./oauth-refresh-families.ts";
3
+ import { loadOAuthRefreshStore } from "./oauth-refresh-families.ts";
4
+ import { exchangeRefreshToken } from "./oauth-refresh-exchange.ts";
5
+ import {
6
+ issueTokenPair,
7
+ saveOAuthStores,
8
+ tokenResponse,
9
+ type OAuthTokenExchangeOptions,
10
+ } from "./oauth-token-issuance.ts";
11
+ import { normalizeOAuthScope, pkceS256, safeEqual } from "./oauth-state.ts";
8
12
 
9
- const ACCESS_TOKEN_TTL_SECONDS = 15 * 60;
10
- const REFRESH_TOKEN_IDLE_TTL_SECONDS = 60 * 60 * 24 * 14;
11
- const REFRESH_TOKEN_FAMILY_TTL_SECONDS = 60 * 60 * 24 * 30;
13
+ export type { OAuthRefreshEvent } from "./oauth-token-issuance.ts";
12
14
  const OAUTH_BODY_LIMIT_BYTES = 64 * 1024;
13
- const MAX_ACCESS_TOKENS = 500;
14
- const MAX_ACCESS_TOKENS_PER_CLIENT = 20;
15
- const MAX_REFRESH_TOKENS = 500;
16
- const MAX_REFRESH_TOKENS_PER_CLIENT = 20;
17
-
18
- type OAuthLock = <T>(callback: () => Promise<T>) => Promise<T>;
19
-
20
- interface OAuthTokenExchangeOptions {
21
- storage: DurableObjectStorage;
22
- tokenVersion: string;
23
- serverName: string;
24
- loadOAuthStore: () => Promise<OAuthStore>;
25
- withLock: OAuthLock;
26
- }
27
-
28
- interface IssuedTokenPair {
29
- accessToken: string;
30
- refreshToken: string;
31
- }
15
+ type AuthorizationCodeStage = "lock" | "load_oauth" | "load_refresh" | "pkce" | "issue" | "persist" | "response";
32
16
 
33
17
  export async function exchangeOAuthToken(
34
18
  request: Request,
@@ -36,9 +20,12 @@ export async function exchangeOAuthToken(
36
20
  options: OAuthTokenExchangeOptions,
37
21
  ): Promise<Response> {
38
22
  const body = await parseRequestBody(request, OAUTH_BODY_LIMIT_BYTES);
23
+ const hasDpop = Boolean(request.headers.get("DPoP"));
24
+ const dpop = hasDpop ? await verifyDpopProof({ request, expectedMethod: "POST", expectedUrl: request.url }) : null;
25
+ if (hasDpop && !dpop) return json({ error: "invalid_dpop_proof" }, 400);
39
26
  const grantType = String(body.grant_type ?? "");
40
- if (grantType === "authorization_code") return exchangeAuthorizationCode(body, base, options);
41
- if (grantType === "refresh_token") return exchangeRefreshToken(body, base, options);
27
+ if (grantType === "authorization_code") return exchangeAuthorizationCode(body, base, options, dpop || undefined);
28
+ if (grantType === "refresh_token") return exchangeRefreshToken(body, base, options, dpop || undefined);
42
29
  return json({ error: "unsupported_grant_type" }, 400);
43
30
  }
44
31
 
@@ -46,172 +33,51 @@ async function exchangeAuthorizationCode(
46
33
  body: Record<string, unknown>,
47
34
  base: string,
48
35
  options: OAuthTokenExchangeOptions,
36
+ dpop?: VerifiedDpopProof,
49
37
  ): Promise<Response> {
50
38
  const code = String(body.code ?? "");
51
39
  const verifier = String(body.code_verifier ?? "");
52
40
  if (!/^[A-Za-z0-9._~-]{43,128}$/.test(verifier)) {
53
41
  return json({ error: "invalid_grant", error_description: "invalid code_verifier" }, 400);
54
42
  }
55
- return options.withLock(async () => {
56
- const oauthStore = await options.loadOAuthStore();
57
- const refreshStore = await loadOAuthRefreshStore(oauthStore, options.storage);
58
- const record = oauthStore.codes[code];
59
- if (!record) return json({ error: "invalid_grant" }, 400);
60
- if (String(body.client_id ?? "") !== record.client_id || String(body.redirect_uri ?? "") !== record.redirect_uri) {
61
- return json({ error: "invalid_grant", error_description: "client or redirect mismatch" }, 400);
62
- }
63
- if (String(body.resource ?? record.resource) !== record.resource || record.resource !== `${base}/mcp`) {
64
- return json({ error: "invalid_target", error_description: "resource mismatch" }, 400);
65
- }
66
- if (!(await safeEqual(await pkceS256(verifier), record.code_challenge))) {
67
- return json({ error: "invalid_grant", error_description: "invalid code_verifier" }, 400);
68
- }
69
- const client = oauthStore.clients[record.client_id];
70
- if (!client) return json({ error: "invalid_grant", error_description: "unknown client" }, 400);
71
-
72
- const issued = await issueTokenPair(oauthStore, refreshStore, record, options.tokenVersion);
73
- delete oauthStore.codes[code];
74
- client.last_used_at = Math.floor(Date.now() / 1000);
75
- await saveOAuthStores(oauthStore, refreshStore, options.storage);
76
- return tokenResponse(issued, record.scope);
77
- });
78
- }
79
-
80
- async function exchangeRefreshToken(
81
- body: Record<string, unknown>,
82
- base: string,
83
- options: OAuthTokenExchangeOptions,
84
- ): Promise<Response> {
85
- const refreshToken = String(body.refresh_token ?? "");
86
- if (!/^mcp_rt_[A-Za-z0-9_-]{43}$/.test(refreshToken)) return json({ error: "invalid_grant" }, 400);
87
- return options.withLock(async () => {
88
- const oauthStore = await options.loadOAuthStore();
89
- const refreshStore = await loadOAuthRefreshStore(oauthStore, options.storage);
90
- const refreshKey = `sha256:${await sha256Hex(refreshToken)}`;
91
- const record = refreshStore.tokens[refreshKey];
92
- if (!record) {
93
- const consumed = refreshStore.consumed[refreshKey];
94
- if (consumed && consumed.expires_at > Math.floor(Date.now() / 1000)) {
95
- revokeOAuthRefreshFamily(oauthStore, refreshStore, consumed.family_id, consumed.expires_at);
96
- await saveOAuthStores(oauthStore, refreshStore, options.storage);
43
+ let stage: AuthorizationCodeStage = "lock";
44
+ try {
45
+ return await options.withLock(async () => {
46
+ stage = "load_oauth";
47
+ const oauthStore = await options.loadOAuthStore();
48
+ stage = "load_refresh";
49
+ const refreshStore = await loadOAuthRefreshStore(oauthStore, options.storage);
50
+ const record = oauthStore.codes[code];
51
+ if (!record) return json({ error: "invalid_grant" }, 400);
52
+ if (String(body.client_id ?? "") !== record.client_id || String(body.redirect_uri ?? "") !== record.redirect_uri) {
53
+ return json({ error: "invalid_grant", error_description: "client or redirect mismatch" }, 400);
97
54
  }
98
- return json({ error: "invalid_grant" }, 400);
99
- }
100
- if (refreshStore.revoked_families[record.family_id]) {
101
- delete refreshStore.tokens[refreshKey];
102
- await options.storage.put(OAUTH_REFRESH_STORE_KEY, refreshStore);
103
- return json({ error: "invalid_grant" }, 400);
104
- }
105
- if (String(body.client_id ?? "") !== record.client_id) {
106
- return json({ error: "invalid_grant", error_description: "client mismatch" }, 400);
107
- }
108
- if (String(body.resource ?? record.resource) !== record.resource || record.resource !== `${base}/mcp`) {
109
- return json({ error: "invalid_target", error_description: "resource mismatch" }, 400);
110
- }
111
- if (body.scope !== undefined && normalizeOAuthScope(body.scope, options.serverName) !== record.scope) {
112
- return json({ error: "invalid_scope" }, 400);
113
- }
114
- if (!options.tokenVersion) {
115
- throw new HttpError(503, "server_error", "OAuth token version is not configured");
116
- }
117
- const account = oauthStore.accounts[record.account_id];
118
- const client = oauthStore.clients[record.client_id];
119
- if (
120
- !record.version
121
- || !(await safeEqual(record.version, options.tokenVersion))
122
- || !account
123
- || !account.active
124
- || account.version !== record.account_version
125
- || account.role !== record.role
126
- || !client
127
- ) {
128
- delete refreshStore.tokens[refreshKey];
129
- await options.storage.put(OAUTH_REFRESH_STORE_KEY, refreshStore);
130
- return json({ error: "invalid_grant" }, 400);
131
- }
132
-
133
- let issued: IssuedTokenPair;
134
- try {
135
- issued = await issueTokenPair(oauthStore, refreshStore, record, options.tokenVersion);
136
- } catch (error) {
137
- if (error instanceof HttpError && error.code === "invalid_grant") {
138
- delete refreshStore.tokens[refreshKey];
139
- await options.storage.put(OAUTH_REFRESH_STORE_KEY, refreshStore);
140
- return json({ error: "invalid_grant" }, 400);
55
+ if (String(body.resource ?? record.resource) !== record.resource || record.resource !== `${base}/mcp`) {
56
+ return json({ error: "invalid_target", error_description: "resource mismatch" }, 400);
141
57
  }
142
- throw error;
143
- }
144
- delete refreshStore.tokens[refreshKey];
145
- recordConsumedRefreshToken(
146
- refreshStore,
147
- refreshKey,
148
- record.family_id,
149
- record.family_expires_at,
150
- Math.floor(Date.now() / 1000),
151
- );
152
- client.last_used_at = Math.floor(Date.now() / 1000);
153
- await saveOAuthStores(oauthStore, refreshStore, options.storage);
154
- return tokenResponse(issued, record.scope);
155
- });
156
- }
157
-
158
- async function issueTokenPair(
159
- oauthStore: OAuthStore,
160
- refreshStore: OAuthRefreshStore,
161
- source: OAuthCode | OAuthRefreshToken,
162
- tokenVersion: string,
163
- ): Promise<IssuedTokenPair> {
164
- if (!tokenVersion) throw new HttpError(503, "server_error", "OAuth token version is not configured");
165
- const now = Math.floor(Date.now() / 1000);
166
- const familyId = "family_id" in source && source.family_id ? source.family_id : randomToken("mcp_family");
167
- const familyExpiresAt = "family_expires_at" in source && source.family_expires_at
168
- ? source.family_expires_at
169
- : now + REFRESH_TOKEN_FAMILY_TTL_SECONDS;
170
- if (familyExpiresAt <= now) throw new HttpError(400, "invalid_grant", "refresh-token family expired");
171
- const accessToken = randomToken("mcp_at");
172
- const refreshToken = randomToken("mcp_rt");
173
- const common = {
174
- client_id: source.client_id,
175
- account_id: source.account_id,
176
- account_version: source.account_version,
177
- role: source.role,
178
- scope: source.scope,
179
- resource: source.resource,
180
- version: tokenVersion,
181
- family_id: familyId,
182
- };
183
- oauthStore.tokens[`sha256:${await sha256Hex(accessToken)}`] = {
184
- ...common,
185
- expires_at: now + ACCESS_TOKEN_TTL_SECONDS,
186
- };
187
- refreshStore.tokens[`sha256:${await sha256Hex(refreshToken)}`] = {
188
- ...common,
189
- family_id: familyId,
190
- family_expires_at: familyExpiresAt,
191
- issued_at: now,
192
- expires_at: Math.min(now + REFRESH_TOKEN_IDLE_TTL_SECONDS, familyExpiresAt),
193
- };
194
- pruneClientRecordByExpiry(oauthStore.tokens, source.client_id, MAX_ACCESS_TOKENS_PER_CLIENT);
195
- pruneRecordByExpiry(oauthStore.tokens, MAX_ACCESS_TOKENS);
196
- pruneClientRecordByExpiry(refreshStore.tokens, source.client_id, MAX_REFRESH_TOKENS_PER_CLIENT);
197
- pruneRecordByExpiry(refreshStore.tokens, MAX_REFRESH_TOKENS);
198
- return { accessToken, refreshToken };
199
- }
200
-
201
- function tokenResponse(issued: IssuedTokenPair, scope: string): Response {
202
- return json({
203
- access_token: issued.accessToken,
204
- refresh_token: issued.refreshToken,
205
- token_type: "Bearer",
206
- expires_in: ACCESS_TOKEN_TTL_SECONDS,
207
- scope,
208
- });
209
- }
210
-
211
- async function saveOAuthStores(
212
- oauthStore: OAuthStore,
213
- refreshStore: OAuthRefreshStore,
214
- storage: DurableObjectStorage,
215
- ): Promise<void> {
216
- await storage.put({ oauth: oauthStore, [OAUTH_REFRESH_STORE_KEY]: refreshStore });
58
+ if (normalizeOAuthScope(record.scope, options.serverName) !== record.scope) {
59
+ return json({ error: "invalid_grant", error_description: "stored scope is invalid" }, 400);
60
+ }
61
+ stage = "pkce";
62
+ if (!(await safeEqual(await pkceS256(verifier), record.code_challenge))) {
63
+ return json({ error: "invalid_grant", error_description: "invalid code_verifier" }, 400);
64
+ }
65
+ const client = oauthStore.clients[record.client_id];
66
+ if (!client) return json({ error: "invalid_grant", error_description: "unknown client" }, 400);
67
+ if (dpop && !(await consumeDpopProof(options.storage, dpop))) return json({ error: "invalid_dpop_proof" }, 400);
68
+ stage = "issue";
69
+ const issued = await issueTokenPair(oauthStore, refreshStore, record, options.tokenVersion, dpop?.jkt);
70
+ delete oauthStore.codes[code];
71
+ client.last_used_at = Math.floor(Date.now() / 1000);
72
+ stage = "persist";
73
+ await saveOAuthStores(oauthStore, refreshStore, options.storage);
74
+ stage = "response";
75
+ return tokenResponse(issued, record.scope, dpop?.jkt);
76
+ });
77
+ } catch (error) {
78
+ if (error instanceof HttpError || (error instanceof Error && error.name.startsWith("oauth_store_persist_"))) throw error;
79
+ const staged = new Error("OAuth authorization-code exchange failed", { cause: error });
80
+ staged.name = `oauth_token_stage_${stage}`;
81
+ throw staged;
82
+ }
217
83
  }
@@ -1,12 +1,24 @@
1
+ import { sanitizePortableLogText } from "../shared/log-redaction.mjs";
2
+
1
3
  const MAX_ERROR_CODES = 64;
2
4
  const MAX_TOOLS = 128;
3
5
  const SENSITIVE_FIELD = /(?:authorization|cookie|credential|password|secret|token|verifier|private[_-]?key)/i;
4
6
 
7
+ export type DaemonTerminalResultDisposition =
8
+ | "committed"
9
+ | "owner_missing_acknowledged"
10
+ | "stale_connection_rejected";
11
+
5
12
  export class WorkerObservability {
6
13
  private readonly startedAt = performance.now();
7
14
  private readonly requests = { total: 0, successful: 0, client_error: 0, server_error: 0 };
8
- private readonly calls = { started: 0, completed: 0, failed: 0, cancelled: 0, timed_out: 0, unmatched_results: 0 };
15
+ private readonly calls = { started: 0, completed: 0, failed: 0, cancelled: 0, timed_out: 0 };
16
+ private readonly terminalResults = {
17
+ committed: 0, owner_missing_acknowledged: 0, stale_connection_rejected: 0,
18
+ };
9
19
  private readonly sockets = { candidates: 0, authenticated: 0, ready: 0, disconnected: 0, protocol_errors: 0 };
20
+ private readonly oauthRefresh = { rotated: 0, retry_issued: 0, retry_exhausted: 0, family_revoked: 0, rejected: 0 };
21
+ private readonly runtimeAlarm = { sets: 0, deletes: 0, noops: 0 };
10
22
  private readonly errors = new Map<string, number>();
11
23
  private readonly tools = new Map<string, { started: number; completed: number; failed: number; active: number }>();
12
24
 
@@ -39,7 +51,10 @@ export class WorkerObservability {
39
51
  this.incrementError(code);
40
52
  }
41
53
 
42
- unmatchedResult(): void { this.calls.unmatched_results += 1; }
54
+ daemonTerminalResult(disposition: DaemonTerminalResultDisposition): void {
55
+ this.terminalResults[disposition] += 1;
56
+ }
57
+ recordError(code: string): void { this.incrementError(code); }
43
58
 
44
59
  socketCandidate(): void { this.sockets.candidates += 1; }
45
60
  socketAuthenticated(): void { this.sockets.authenticated += 1; }
@@ -50,12 +65,30 @@ export class WorkerObservability {
50
65
  this.incrementError(code || "protocol_error");
51
66
  }
52
67
 
68
+ oauthRefreshEvent(event: "rotated" | "retry_issued" | "retry_exhausted" | "family_revoked" | "rejected"): void {
69
+ this.oauthRefresh[event] += 1;
70
+ }
71
+
72
+ runtimeAlarmMutation(action: "set" | "delete" | "noop"): void {
73
+ if (action === "set") this.runtimeAlarm.sets += 1;
74
+ else if (action === "delete") this.runtimeAlarm.deletes += 1;
75
+ else this.runtimeAlarm.noops += 1;
76
+ }
77
+
53
78
  snapshot(): Record<string, unknown> {
54
79
  return {
55
80
  uptime_ms: Math.max(0, performance.now() - this.startedAt),
81
+ metric_scope: {
82
+ lifecycle: "current_worker_isolate",
83
+ request_scoped_calls: true,
84
+ counters_may_not_balance_across_isolate_restarts: true,
85
+ },
56
86
  requests: { ...this.requests },
57
87
  calls: { ...this.calls },
88
+ terminal_results: { ...this.terminalResults },
58
89
  sockets: { ...this.sockets },
90
+ oauth_refresh: { ...this.oauthRefresh },
91
+ runtime_alarm: { ...this.runtimeAlarm },
59
92
  errors: Object.fromEntries([...this.errors.entries()].sort(([left], [right]) => left.localeCompare(right))),
60
93
  tools: Object.fromEntries([...this.tools.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([name, metric]) => [name, { ...metric }])),
61
94
  };
@@ -63,11 +96,11 @@ export class WorkerObservability {
63
96
 
64
97
  event(level: "info" | "warn" | "error", event: string, fields: Record<string, unknown> = {}): void {
65
98
  const entry = {
99
+ ...sanitizeFields(fields),
66
100
  timestamp: new Date().toISOString(),
67
101
  level,
68
102
  component: "worker",
69
103
  event: sanitizeName(event),
70
- ...sanitizeFields(fields),
71
104
  };
72
105
  const text = JSON.stringify(entry);
73
106
  if (level === "error") console.error(text);
@@ -99,7 +132,7 @@ function sanitizeName(value: unknown): string {
99
132
  }
100
133
 
101
134
  function sanitizeFields(fields: Record<string, unknown>): Record<string, unknown> {
102
- const out: Record<string, unknown> = {};
135
+ const out = Object.create(null) as Record<string, unknown>;
103
136
  for (const [key, value] of Object.entries(fields).slice(0, 32)) {
104
137
  const safeKey = sanitizeName(key);
105
138
  if (!safeKey) continue;
@@ -108,7 +141,7 @@ function sanitizeFields(fields: Record<string, unknown>): Record<string, unknown
108
141
  continue;
109
142
  }
110
143
  if (typeof value === "boolean" || typeof value === "number" || value === null) out[safeKey] = value;
111
- else if (typeof value === "string") out[safeKey] = value.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 256);
144
+ else if (typeof value === "string") out[safeKey] = sanitizePortableLogText(value, { maxChars: 256 });
112
145
  }
113
146
  return out;
114
147
  }
@@ -0,0 +1,15 @@
1
+ export class PendingAdmissionGate {
2
+ private tail: Promise<void> = Promise.resolve();
3
+
4
+ async run<T>(operation: () => T | Promise<T>): Promise<T> {
5
+ let release: () => void = () => {};
6
+ const predecessor = this.tail;
7
+ this.tail = new Promise<void>((resolve) => { release = resolve; });
8
+ await predecessor;
9
+ try {
10
+ return await operation();
11
+ } finally {
12
+ release();
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,108 @@
1
+ import {
2
+ CONTROL_PLANE_TOOL_NAMES,
3
+ toolCallAdmission,
4
+ toolCallCapacityConfig,
5
+ toolCallCapacityUsage,
6
+ type ToolCallAdmission,
7
+ type ToolCallCapacityConfig,
8
+ } from "../shared/tool-call-capacity.mjs";
9
+ import { WorkerToolError } from "./errors.ts";
10
+ import type { PendingCallRecord } from "./pending-call-contract.ts";
11
+
12
+ export { CONTROL_PLANE_TOOL_NAMES };
13
+
14
+ export const MAX_PENDING_CALLS = 32;
15
+ export const RESERVED_CONTROL_PENDING_CALLS = 2;
16
+ export const WORKER_PENDING_CALL_CAPACITY = toolCallCapacityConfig(
17
+ MAX_PENDING_CALLS,
18
+ RESERVED_CONTROL_PENDING_CALLS,
19
+ CONTROL_PLANE_TOOL_NAMES,
20
+ );
21
+ export const WORKER_PENDING_REGISTRY_OPTIONS = Object.freeze({
22
+ reservedCapacity: RESERVED_CONTROL_PENDING_CALLS,
23
+ reservedTools: CONTROL_PLANE_TOOL_NAMES,
24
+ });
25
+
26
+
27
+ export type PendingRegistrySnapshot = PendingCapacitySnapshot & {
28
+ detached: number;
29
+ request_keys: number;
30
+ maximum: number;
31
+ ordinary_capacity: number;
32
+ reserved_capacity: number;
33
+ active_reserved: number;
34
+ active_ordinary: number;
35
+ oldest_ms: number;
36
+ };
37
+
38
+ export function pendingRegistrySnapshot(
39
+ records: Iterable<PendingCallRecord>,
40
+ requestKeyCount: number,
41
+ now: number,
42
+ config: ToolCallCapacityConfig,
43
+ ): PendingRegistrySnapshot {
44
+ const byTool = Object.create(null) as Record<string, number>;
45
+ let active = 0;
46
+ let detached = 0;
47
+ let oldestMs = 0;
48
+ for (const record of records) {
49
+ active += 1;
50
+ byTool[record.tool] = (byTool[record.tool] ?? 0) + 1;
51
+ detached += Number(!record.socket);
52
+ oldestMs = Math.max(oldestMs, now - record.startedAt);
53
+ }
54
+ const usage = toolCallCapacityUsage({ active, byTool }, config);
55
+ return {
56
+ active,
57
+ detached,
58
+ request_keys: requestKeyCount,
59
+ maximum: usage.maximum,
60
+ ordinary_capacity: usage.ordinaryMaximum,
61
+ reserved_capacity: usage.reserved,
62
+ active_reserved: usage.activeReserved,
63
+ active_ordinary: usage.activeOrdinary,
64
+ oldest_ms: oldestMs,
65
+ by_tool: byTool,
66
+ };
67
+ }
68
+
69
+ export type PendingCapacitySnapshot = {
70
+ active: number;
71
+ by_tool: Record<string, number>;
72
+ };
73
+
74
+ export function pendingCallAdmission(
75
+ pending: PendingCapacitySnapshot,
76
+ tool: string,
77
+ config: ToolCallCapacityConfig = WORKER_PENDING_CALL_CAPACITY,
78
+ ): ToolCallAdmission {
79
+ return toolCallAdmission(pending, config, tool);
80
+ }
81
+
82
+ export function pendingCapacityProjection(
83
+ pending: PendingRegistrySnapshot,
84
+ preDispatch: PendingCapacitySnapshot,
85
+ ): PendingRegistrySnapshot & Record<string, unknown> {
86
+ const byTool = { ...pending.by_tool };
87
+ for (const [tool, count] of Object.entries(preDispatch.by_tool)) byTool[tool] = (byTool[tool] ?? 0) + count;
88
+ const usage = toolCallCapacityUsage({ active: pending.active + preDispatch.active, byTool }, WORKER_PENDING_CALL_CAPACITY);
89
+ return Object.freeze({
90
+ ...pending,
91
+ pre_dispatch_waiters: preDispatch.active,
92
+ capacity_active: usage.active,
93
+ capacity_active_ordinary: usage.activeOrdinary,
94
+ capacity_active_reserved: usage.activeReserved,
95
+ });
96
+ }
97
+
98
+ export function assertWorkerPendingCallAdmission(
99
+ pending: PendingCapacitySnapshot,
100
+ tool: string,
101
+ ): void {
102
+ const decision = pendingCallAdmission(pending, tool);
103
+ if (decision.allowed) return;
104
+ const message = decision.reason === "ordinary_capacity"
105
+ ? `ordinary daemon-call capacity reached (${decision.ordinaryMaximum}); control-plane capacity is reserved for diagnosis and recovery`
106
+ : "too many concurrent daemon tool calls";
107
+ throw new WorkerToolError("limit_exceeded", message, true);
108
+ }