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