machine-bridge-mcp 3.0.0-beta.198 → 3.0.0-beta.199

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 (150) hide show
  1. package/CHANGELOG.md +112 -30
  2. package/README.md +4 -3
  3. package/SECURITY.md +2 -2
  4. package/SUPPORT.md +2 -0
  5. package/browser-extension/broker-auth.js +5 -3
  6. package/browser-extension/browser-operation-contract.js +92 -0
  7. package/browser-extension/browser-operations.js +8 -103
  8. package/browser-extension/manifest.json +1 -2
  9. package/browser-extension/page-automation.js +2 -2
  10. package/browser-extension/pairing-bootstrap.js +6 -7
  11. package/browser-extension/pairing.js +14 -4
  12. package/browser-extension/service-worker.js +6 -4
  13. package/docs/AUDIT.md +6 -24
  14. package/docs/COMPUTER_USE.md +6 -6
  15. package/docs/LOCAL_AUTHORIZATION.md +2 -2
  16. package/docs/LOCAL_AUTOMATION.md +6 -2
  17. package/docs/LOGGING.md +6 -2
  18. package/docs/MANAGED_JOBS.md +5 -5
  19. package/docs/MULTI_ACCOUNT.md +2 -2
  20. package/docs/OPERATIONS.md +10 -10
  21. package/docs/OVERVIEW.md +1 -1
  22. package/docs/PRIVACY.md +5 -1
  23. package/docs/THREAT_MODEL.md +3 -1
  24. package/docs/TOOL_REFERENCE.md +4 -8
  25. package/docs/UPGRADING.md +4 -2
  26. package/package.json +3 -3
  27. package/scripts/consumer-package-security.mjs +7 -3
  28. package/scripts/prerelease-activation.mjs +22 -9
  29. package/scripts/privacy-check.mjs +99 -8
  30. package/scripts/promotion-digest.mjs +11 -7
  31. package/scripts/publish-npm.mjs +2 -4
  32. package/scripts/release-acceptance.mjs +12 -8
  33. package/scripts/release-candidate-manifest.mjs +12 -9
  34. package/scripts/release-impact-check.mjs +2 -1
  35. package/scripts/release-oauth-canary-core.mjs +16 -9
  36. package/scripts/release-oauth-canary-evidence.mjs +10 -8
  37. package/scripts/release-soak.mjs +14 -11
  38. package/src/local/account-access.mjs +2 -1
  39. package/src/local/account-admin.mjs +20 -11
  40. package/src/local/authority-context.mjs +10 -6
  41. package/src/local/browser-bridge.mjs +2 -9
  42. package/src/local/browser-broker-auth.mjs +9 -8
  43. package/src/local/browser-extension-identity.mjs +4 -2
  44. package/src/local/browser-operation-service.mjs +1 -1
  45. package/src/local/browser-pairing-grant.mjs +35 -35
  46. package/src/local/browser-pairing-http.mjs +2 -2
  47. package/src/local/browser-pairing-launch.mjs +4 -4
  48. package/src/local/browser-pairing-store.mjs +64 -66
  49. package/src/local/call-authority.mjs +14 -5
  50. package/src/local/cli-account-admin.mjs +1 -2
  51. package/src/local/cli-local-admin.mjs +34 -3
  52. package/src/local/cli-policy.mjs +1 -1
  53. package/src/local/cli.mjs +2 -1
  54. package/src/local/computer-use-arguments.mjs +2 -6
  55. package/src/local/computer-use.mjs +1 -1
  56. package/src/local/daemon-http-relay-connection.mjs +2 -2
  57. package/src/local/daemon-http-relay-request.mjs +8 -1
  58. package/src/local/device-identity.mjs +34 -21
  59. package/src/local/git-version.mjs +11 -0
  60. package/src/local/hardened-npm-download.mjs +2 -2
  61. package/src/local/hardened-npm.mjs +9 -7
  62. package/src/local/job-runner.mjs +54 -45
  63. package/src/local/macos-trust-broker.mjs +15 -10
  64. package/src/local/managed-job-active-child.mjs +10 -8
  65. package/src/local/managed-job-dependencies.mjs +4 -4
  66. package/src/local/managed-job-listing.mjs +2 -2
  67. package/src/local/managed-job-lock.mjs +20 -17
  68. package/src/local/managed-job-plan.mjs +4 -2
  69. package/src/local/managed-job-projection.mjs +8 -2
  70. package/src/local/managed-job-recovery-history.mjs +49 -0
  71. package/src/local/managed-job-relaunch.mjs +8 -7
  72. package/src/local/managed-job-retention-policy.mjs +5 -5
  73. package/src/local/managed-job-runner-claim.mjs +17 -12
  74. package/src/local/managed-job-runner-liveness.mjs +14 -5
  75. package/src/local/managed-job-state-validation.mjs +2 -0
  76. package/src/local/managed-job-terminal-maintenance.mjs +3 -2
  77. package/src/local/managed-job-terminal.mjs +4 -3
  78. package/src/local/managed-job-transient-recovery-capacity.mjs +7 -7
  79. package/src/local/managed-jobs.mjs +33 -22
  80. package/src/local/operation-risk.mjs +12 -0
  81. package/src/local/owner-state-lock.mjs +4 -4
  82. package/src/local/process-identity.mjs +25 -8
  83. package/src/local/process-tracker.mjs +20 -8
  84. package/src/local/process-tree-ownership.mjs +1 -1
  85. package/src/local/relay-connection-support.mjs +2 -1
  86. package/src/local/remote-configuration.mjs +4 -2
  87. package/src/local/resource-admission.mjs +13 -6
  88. package/src/local/resource-lease-accounting.mjs +1 -1
  89. package/src/local/resource-process-ancestry.mjs +1 -1
  90. package/src/local/resource-request-contract.mjs +3 -2
  91. package/src/local/resource-staging-recovery.mjs +11 -8
  92. package/src/local/resource-transaction-lock.mjs +4 -4
  93. package/src/local/resource-waiters.mjs +8 -9
  94. package/src/local/runtime-diagnostic-projection.mjs +1 -1
  95. package/src/local/runtime-package-identity.mjs +9 -8
  96. package/src/local/runtime.mjs +3 -3
  97. package/src/local/security-audit-activity.mjs +22 -8
  98. package/src/local/security-audit-beta198-migration.mjs +50 -0
  99. package/src/local/security-audit-dispatch.mjs +23 -11
  100. package/src/local/security-audit-gate.mjs +38 -0
  101. package/src/local/security-audit-log.mjs +20 -16
  102. package/src/local/security-audit-state.mjs +57 -20
  103. package/src/local/security-audit-storage.mjs +21 -12
  104. package/src/local/service-environment.mjs +4 -4
  105. package/src/local/state-inventory.mjs +4 -4
  106. package/src/local/state-root-owned-namespaces.mjs +0 -11
  107. package/src/local/state.mjs +32 -42
  108. package/src/local/tool-executor.mjs +23 -6
  109. package/src/local/worker-deployment-fingerprint.mjs +13 -2
  110. package/src/local/worker-health.mjs +7 -4
  111. package/src/local/worker-identity-contract.mjs +14 -0
  112. package/src/local/worker-secret-file.mjs +18 -6
  113. package/src/local/workspace-profile-migration.mjs +3 -4
  114. package/src/local/wrangler-toolchain/package-lock.json +118 -118
  115. package/src/local/wrangler-toolchain/package.json +3 -1
  116. package/src/local/wrangler-toolchain-installed-tree.mjs +42 -0
  117. package/src/local/wrangler-toolchain-verification.mjs +19 -33
  118. package/src/local/wrangler-toolchain.mjs +13 -2
  119. package/src/shared/admin-auth.mjs +6 -7
  120. package/src/shared/daemon-auth.mjs +6 -7
  121. package/src/shared/device-session-auth.mjs +8 -7
  122. package/src/shared/log-redaction.mjs +1 -1
  123. package/src/shared/server-metadata.json +3 -3
  124. package/src/shared/tool-catalog.json +4 -8
  125. package/src/worker/access.ts +2 -1
  126. package/src/worker/account-admin.ts +7 -3
  127. package/src/worker/daemon-auth.ts +8 -8
  128. package/src/worker/daemon-http-protocol.ts +1 -1
  129. package/src/worker/daemon-liveness.ts +9 -3
  130. package/src/worker/daemon-socket-attachment.ts +1 -1
  131. package/src/worker/device-session-verifier.ts +10 -6
  132. package/src/worker/dpop.ts +1 -1
  133. package/src/worker/index.ts +1 -1
  134. package/src/worker/managed-job-hosted-authority.ts +3 -3
  135. package/src/worker/mcp-controller.ts +3 -3
  136. package/src/worker/{mcp-stale-schema-compat.ts → mcp-stale-schema-guidance.ts} +1 -1
  137. package/src/worker/mcp-subscription-capacity.ts +3 -3
  138. package/src/worker/oauth-client-admin.ts +2 -4
  139. package/src/worker/oauth-client-contract.ts +3 -4
  140. package/src/worker/oauth-controller.ts +3 -17
  141. package/src/worker/oauth-refresh-exchange.ts +7 -4
  142. package/src/worker/oauth-refresh-families.ts +9 -11
  143. package/src/worker/oauth-refresh-persistence.ts +6 -17
  144. package/src/worker/oauth-state.ts +11 -41
  145. package/src/worker/oauth-store-validation.ts +14 -10
  146. package/src/worker/oauth-tokens.ts +10 -7
  147. package/src/worker/pending-call-capacity.ts +1 -1
  148. package/src/worker/worker-continuity-evidence.ts +5 -6
  149. package/src/worker/worker-rate-limit-key.ts +1 -1
  150. package/src/local/resource-legacy-staging-recovery.mjs +0 -71
package/CHANGELOG.md CHANGED
@@ -1,36 +1,118 @@
1
1
  # Changelog
2
2
 
3
- ## 3.0.0-beta.198 - 2026-10-03
3
+ ## 3.0.0-beta.199 - 2026-10-08
4
4
 
5
- - Redact macOS temporary-path aliases and truncated Windows case aliases from managed-job output.
6
- - Decode sensitive URL parameter names before log redaction; redact complete and truncated private-key bodies.
7
- - Reject coerced Cloudflare account identifiers and malformed token-validity evidence.
8
- - Stream deployment-source enumeration with entry, depth, file-count and byte budgets.
9
- - Persist confirmed Worker upload evidence before endpoint parsing or staging cleanup can fail; reconcile unknown endpoints instead of automatically uploading again.
10
- - Consolidate duplicate engineering release guidance under the canonical releasing procedure.
11
- - Drain durable authority revocations in bounded HTTPS batches without losing acknowledgements or invalidating healthy channels; include sequence/envelope overhead in response budgets and atomically persist replay-capacity family eviction with its revocation.
12
- - Preserve replacement WebSocket connection deadlines when stale events arrive, and settle asynchronous gateway/signature failures through typed responses.
13
- - Reject malformed revocation/OAuth/DPoP inputs, non-boolean account activation values and inherited enum keys; revalidate current authority and respect pre-existing cancellation before daemon dispatch.
14
- - Preserve monitor transitions across sequence rollover and bound observability overflow without recursive failure.
15
- - Bind instruction/skill reads, file rollback, lock recovery, directory traversal, SSH publication and state cleanup to verified identities; validate config paths before reading and preserve unrelated replacements and failure evidence.
16
- - Keep process, session and runtime ownership through asynchronous settlement and retain candidate daemon ownership after failed shutdown; close late-spawn shutdown races, terminate SSE encoding/projection failures, bound invocation inputs, and give isolated jobs private runtime directories.
17
- - Fence browser renderer mutations by cancellation/deadline authority, including asynchronous actionability waits and socket replacement.
18
- - Verify Secure Enclave provenance before native export, signing or deletion; reject oversized signing input before EOF or Keychain access.
19
- - Sanitize default CLI errors and reject incomplete, failed, inconsistent or scalar-coerced SARIF evidence; prevent conformance credentials from reaching child environments and release proxy readers after disconnect.
20
- - Implement the declared empty resource-template list, remove stale resource conformance exclusions, and keep unknown-resource errors bounded without reflecting their URI.
21
- - Apply path-display policy before skill warning sanitization/truncation and preserve dollar signs as literal text in path projections and file edits.
22
- - Harden generated Worker type recovery with bounded no-follow reads; share strict consumer/deployment audit validation and fixed Worker persistence error classes without copying arbitrary exception names.
23
- - Wait for the full-access fixture's verified runner exit before deleting job state, restore its environment on shutdown failure, and use the production Swift driver for native provenance tests.
24
- - Remove arbitrary peer-supplied WebSocket close reasons from debug logs while retaining fixed protocol classifications and outage recovery summaries.
25
- - Use locked dependency installation in the Windows checkout launcher and fail on setup errors without stale exit-code expansion; share private npm launchers across CI and temporary toolchains so nested lifecycle calls retain the pinned CLI.
26
- - Correct the public validator's readonly-array declaration and inspect dependencies through one syntax-aware source graph, including escaped paths and declaration files.
27
- - Share bounded child readiness, capture and exit cleanup across managed-job/process-lock fixtures, with one deadline for preparation and settlement. Prove recovery-lock handoff before corruption assertions and descendant readiness before publication timeout assertions; await runtime teardown and consolidate screenshot rejection setup while retaining distinct cleanup assertions and browser limits.
28
- - Require both markers in source-order guards and share exact script checks/source reads. Reject event-derived shell expressions with a bounded scan and alternate YAML mappings before dependency-free workflow policy reads.
29
- - Recheck Git metadata directory identity after opening and traversal, and preserve cancellation during empty-directory opening.
30
- - Share bounded browser screenshot validation and one observation decode; assert fixture outcomes and clear failed-handshake deadlines.
31
- - Reject coerced or malformed consumer dependency/SBOM evidence and duplicate graph edges without string normalization.
32
- - Replace the repeated test-case inventory with a current regression map, and remove repeated overview/release/install instructions while retaining canonical recovery, authority and privacy contracts.
5
+ - Resolve GHSA-wq5f-xc86-pv6w (CVE-2026-96889) by pinning Sharp 0.35.5 in both the source checkout and the isolated Cloudflare deployment toolchain, including their independent dependency locks and lifecycle-script allowlists.
6
+ - Require the installed private toolchain dependency tree and its integrity marker to match the patched Sharp version; reject vulnerable installed versions before deployment tooling is used.
7
+ - Raise the consumer-package Sharp minimum to the first patched version and add vulnerable-version and patched-version regression cases.
8
+ - Fail closed on malformed private toolchain npm versions, dependency-tree metadata and coerced package versions; avoid reflecting raw private npm diagnostics in errors.
9
+ - Preserve fail-closed npm audit, supply-chain verification and OpenSSF Scorecard review gates without adding a vulnerability exception.
10
+ - Remove the obsolete boolean `computer_act.include_post_screenshot` compatibility option; require `post_screenshot=auto|always|never`, reject obsolete inputs, and advance the tool schema generation.
11
+ - Replace destructive state-root removal advice on obsolete persisted schemas with preservation and supported upgrade/backup recovery guidance.
12
+ - Keep managed-job fatal operational diagnostics to a fixed error classification, avoiding arbitrary exception-message disclosure to logs.
13
+ - Extend that fixed-diagnostic boundary through runner startup before the job claim is confirmed, so missing or unreadable initial state cannot expose private paths, raw filesystem errors or exception stacks.
14
+ - Require exact JSON types for persisted process ownership, runner claims, active-child claims, resource transaction locks, leases and waiters; reject string/array coercion before liveness, recovery, cleanup, termination or critical-section decisions.
15
+ - Require exact string credentials, integer ports and string broker endpoints for current browser-pairing state; arrays and numeric-string ports no longer acquire pairing or authentication authority through JavaScript conversion.
16
+ - Remove the retired browser-pairing migration marker from the current persisted model and advance the pairing schema to version 3, while preserving one exact one-way beta.198 stable-state upgrade that retains both credentials and the selected port; pending beta.198 migrations and older, malformed, coercible or extra-field envelopes remain fail closed.
17
+ - Remove the redundant managed-job result `recovered` compatibility boolean; recovery outcome is represented only by the terminal status enum, and interrupted recovery never replays main steps.
18
+ - Require Git 2.50 or later for reachable-history privacy scanning instead of carrying an older `rev-list` output parser; fail before scanning when the trusted Git version is too old or malformed.
19
+ - Reject non-string OAuth grant types, authorization codes, PKCE verifiers, client identifiers, redirect URIs, resources, scopes and refresh tokens before credential lookup or consumption.
20
+ - Apply the Sharp 0.35.5 minimum independently to consumer CycloneDX validation as well as the installed dependency tree, including malformed and prerelease version rejection.
21
+ - Reject coercible legacy resource-staging publisher PID evidence instead of accepting strings, arrays or booleans as process identities; retain the staging files on malformed ownership.
22
+ - Refuse staging reclamation when the publisher PID encoded in a current-format filename exceeds safe integer bounds, rather than treating invalid process identity as permission to delete.
23
+ - Remove the retired version-2 capability-lease/release-task cleanup compatibility surface from current documentation and state-root removal; unsupported older state is preserved and rejected instead of interpreted by the current runtime.
24
+ - Reject non-string browser extension IDs, origins, and manifest public keys instead of allowing arrays or other values to acquire extension identity through JavaScript string coercion.
25
+ - Treat synchronous security-audit worker transport failures as terminal worker failures, settle all pending audit promises, mark health unavailable, and account for records that could not be persisted instead of leaving queued callers hanging.
26
+ - Stop interpreting retired schema-v1 Worker continuity evidence; unsupported persisted continuity schemas now fail closed to empty current-schema diagnostics instead of partially migrating historical counters.
27
+ - Reject non-string persisted daemon socket roles instead of allowing arrays or other values to pass role validation through JavaScript string coercion.
28
+ - Add a random per-daemon pseudonym correlation epoch to new security-audit events so incident review can identify runtime-key rotation boundaries without creating stable cross-restart principal or target identifiers.
29
+ - Remove the redundant browser-extension `tabs` permission; existing `<all_urls>` host authority already supplies tab metadata for scriptable pages, while restricted browser-internal pages now expose less metadata to the extension.
30
+ - Route npm publication cleanup warnings through the shared release diagnostic sanitizer so a successful publication cannot leak local home paths or credential-shaped values through best-effort cleanup messages.
31
+ - Mark browser-derived page text, accessibility labels, DOM attributes, and screenshots as untrusted data in remote MCP server instructions so webpage prompt injection is not presented as authorization or higher-precedence guidance.
32
+ - Repair the privacy regression fixture so Git-version boundary assertions use Node's strict assertion API instead of the test file's boolean-only helper, restoring coverage-gate execution of the privacy suite.
33
+ - Remove schema-v1 migration rationale from the always-injected MCP server instructions now that the runtime is current-schema-only; historical rationale remains in release history instead of consuming every client context.
34
+ - Reject Git version components outside JavaScript safe-integer bounds so malformed trusted-Git version output cannot satisfy the privacy-history minimum through numeric infinity coercion.
35
+ - Keep promotion-digest npm pack metadata type-exact by rejecting non-string paths and non-integer file modes instead of normalizing coercible values into release evidence.
36
+ - Replace stale fixed beta.1 commands in the release guide with an explicit next-version variable and a soak-confirmation command derived from the current package version, preventing copy/paste of an obsolete prerelease identifier.
37
+ - Reject non-string local account-admin lookup targets instead of allowing arrays or other values to select a privileged account through string coercion.
38
+ - Require persisted resource lease, waiter, transaction-lock, and newly bound child PIDs to be positive safe integers so precision-losing process identities fail as integrity errors instead of being silently reclaimed or bound.
39
+ - Apply the same safe-integer PID boundary to the shared owner-state lock primitive so malformed toolchain and maintenance ownership evidence cannot be reclaimed after precision loss.
40
+ - Preserve an in-flight browser pairing exchange after invalid final challenge, nonce, or proof attempts; the one-time grant is now consumed only after the final HMAC proof succeeds, preventing unauthenticated failures from deleting a legitimate pending exchange.
41
+ - Require service network/TLS environment values to remain native strings on capture and reload so malformed persisted arrays or numbers cannot be coerced into active proxy or certificate configuration.
42
+ - Make shared device-session and daemon-authentication transcript builders type-exact so arrays, numeric strings, or other coercible values cannot acquire signed protocol semantics if a future caller omits its own pre-validation.
43
+ - Keep process-tree ownership snapshots type-exact by accepting only positive safe-integer child PIDs, matching persisted active-child and lock ownership boundaries.
44
+ - Advance security-audit state to schema 3 while retaining one exact, one-way beta.198 schema-1 data migration: prior event objects and hashes remain unchanged under a schema-3 envelope with an explicit legacy-prefix count, while all newly appended events require current pseudonym, operation-correlation, and settlement fields.
45
+ - Split browser operation scalar/response-contract validation into a fixed preloaded extension module and lower the browser-operations responsibility budget, removing its near-limit 1900-line concentration without changing dispatch semantics.
46
+ - Stop reclaiming malformed daemon/startup/maintenance and managed-job lock files by age alone. Current lock publication is complete-before-visible, so malformed owner metadata is now retained as an integrity blocker; only structurally valid owners with provably stale process identity are automatically reclaimed.
47
+ - Keep browser pairing types exact after the URL parsing boundary: the service worker and HMAC bootstrap now require native integer ports, string grants, and numeric clocks instead of accepting coercible arrays or numeric strings.
48
+ - Gate remote non-read-only dispatch on durable security-audit health and a persisted `dispatch_intent`. Intent and terminal records share a runtime-private operation reference; the intent deliberately proves only that the authorized pre-dispatch boundary was reached, so a timeout cannot be misread as proof that the handler started.
49
+ - Make the audit pre-dispatch gate consume an explicit local side-effect classification rather than MCP annotation metadata, and regression-check public `readOnlyHint` values against that security classification so catalog drift cannot silently weaken the gate.
50
+ - Expose whether managed-job recovery attribution is complete without inventing beta.198 history: beta.199 recovery entries must form a contiguous suffix ending at the current attempt count, while prior-version attempt counts with no persisted reason/time remain explicitly `recovery_history_complete=false`.
51
+ - Keep daemon relay targets type-exact from persisted/runtime configuration through WebSocket and HTTPS fallback: non-string Worker URLs are rejected rather than being coerced into authenticated network origins, and malformed HTTP relay URLs use one fixed error classification.
52
+ - Apply the same exact-string Worker origin/name boundary to deployment health verification so malformed persisted arrays cannot be normalized into healthy Worker identity evidence.
53
+ - Keep current security-audit hashes, pseudonym epochs, timestamps, and numeric counters type-exact; arrays and numeric strings no longer become valid-looking audit evidence through regular-expression or numeric coercion.
54
+ - Tighten both current and beta.198 audit readers to the actual writer image: canonical ISO timestamps, bounded token/text forms, and non-negative safe-integer duration/byte counters. Hash-consistent values that no supported writer could emit are rejected as integrity failures.
55
+ - Reject precision-losing process IDs in resource ancestry sampling and lease accounting so malformed `ps`/PowerShell evidence cannot collapse distinct PIDs into one JavaScript number.
56
+ - Retire pre-beta.198 temporary Worker-secret filename compatibility. Cleanup now recognizes only the current PID + creation-time + process-start-time + 12-hex nonce shape; obsolete lookalike secret files are retained as explicit integrity blockers instead of being weakly attributed or silently ignored.
57
+ - Keep local device-root/session authentication primitives type-exact: public roots now pass shared canonical P-256 validation, authentication clocks require native finite numbers, and session-signing transcripts require native strings rather than coercible values.
58
+ - Preserve security-audit evidence types end to end: the daemon no longer coerces injected audit clocks before persistence, and recent-activity projection no longer normalizes string/array timestamps or byte counters into valid-looking evidence.
59
+ - Keep managed-job retention timestamps type-exact before destructive maintenance: malformed staged `created_at` values no longer gain expiry authority through string coercion, and terminal retention ordering only parses native timestamp strings.
60
+ - Make account-administration authentication transcripts and local request construction type-exact: origin/method/path/hash/key/nonce fields no longer accept coercible values, signed body bytes must already be a string, and display names are rejected rather than stringified.
61
+ - Require daemon candidate/readiness/liveness timestamps to be native canonical ISO strings before they can establish freshness or alarm deadlines; JavaScript-coercible arrays and alternate timestamp spellings no longer become liveness evidence.
62
+ - Document the existing `file:` browser-automation privacy boundary explicitly: when Chromium file-URL access is enabled, local file-backed tabs are data-bearing automation surfaces rather than ordinary harmless tab metadata.
63
+ - Add an explicit offline `machine-mcp browser reset` revocation path. It serializes through state maintenance, refuses active runtime/job ownership, rechecks that the loopback broker is stopped, and atomically rotates both broker HMAC credentials without exposing their replacements.
64
+ - Correct the browser-pairing storage model in the threat/automation docs: authoritative broker credentials remain in owner-only Machine Bridge state, while the extension stores its extension-token copy in Chromium local extension storage for restart persistence and may retain an unusable stale copy after local state reset.
65
+ - Keep the macOS Secure Enclave broker boundary type-exact: configured/provisioned broker paths and signing transcripts now require native strings, preventing coercible arrays or objects from acquiring trust-broker path or signature semantics.
66
+ - Redact browser pairing grants and managed-job recovery/control credentials by URL parameter name at the final logging boundary, including extension/runtime token parameters, while leaving harmless names such as `grant_type` intact.
67
+ - Keep browser pairing grant and launch clocks/ports type-exact and internally consistent: native safe integers are required, one wall-clock sample governs each bootstrap transition, and grant creation rejects timestamps that cannot produce the protocol's canonical 13-digit expiry.
68
+ - Tighten process-identity evidence to the current writer/probe image: snapshot PIDs must be safe integers, snapshot start times must already be numeric, and persisted owner timestamps must be canonical ISO strings rather than merely parseable dates.
69
+ - When browser history-state cleanup fails on the ephemeral pairing page, replace the current location with the token-free `/pair` URL before stopping; pairing still fails closed, but the short-lived grant no longer remains indefinitely in the visible fragment/history entry.
70
+ - Clear pairing bootstrap material from the content-script closure immediately after successful or ordinary failed pairing; retain it only for the explicit short-lived manual-repair branch that still needs the same grant.
71
+ - On POSIX, synchronize the security-audit parent directory after atomic state replacement before acknowledging a batch, so a pre-dispatch intent is not reported persisted solely because the temporary file contents reached storage before the rename metadata.
72
+ - Require the signed DPoP `htm` claim to match the normalized expected HTTP method exactly instead of uppercasing the proof claim, aligning the verifier with RFC 9449-style exact method binding and mature OAuth implementations.
73
+ - Clarify incident diagnostics so recent audit `pending/unknown` effect-settlement counts are not mistaken for a live termination queue; current process termination remains a separate runtime snapshot.
74
+ - Restore the local CLI browser-pairing launcher helper after compatibility-copy cleanup removed its imports while leaving the default dependency reference in place; CLI startup and `--version` no longer fail before argument dispatch.
75
+ - Update the page-automation VM regression harness for browser-global Web Crypto and automation API version 6, keeping the production document epoch on `crypto.randomUUID()` instead of weakening it for Node test contexts.
76
+ - Remove stale version-2 lease and schema-1 migration rationale from current architecture, operations, and release guidance now that those compatibility readers and surfaces are no longer supported.
77
+ - Expand the privacy publication scan beyond Git-visible files to every literal `package.json#files` tree, closing a gap where Git-ignored files such as `src/local/*.local` were still included by `npm pack` but escaped the privacy gate.
78
+ - Require canonical current account roles in every persisted OAuth authority record and remove the online bad-role repair branch; malformed or non-canonical role state now fails the current-schema boundary instead of being rewritten at runtime.
79
+ - Remove the unreachable local-admin external-launch helper and its process-spawn/browser-launch dependencies, leaving the current pairing launch path owned only by the browser pairing service.
80
+ - Reject prototype-shaped or otherwise malformed account IDs before indexing the persisted account table, and require runtime tool-handler dispatch to use own properties only.
81
+ - Extend the publication privacy gate across npm's implicit README/LICENSE/package/main/bin/man surfaces, closing a second gap where a Git-ignored root README could still be auto-included by `npm pack`.
82
+ - Document the residual same-user ancestor-directory race for ordinary workspace file mutations instead of overstating path canonicalization as an atomic cross-platform directory-handle boundary.
83
+ - Document that the owner-only autostart network snapshot can persist embedded proxy authentication and private certificate paths even though those values are excluded from status output and logs.
84
+ - Reject malformed hosted managed-job dependency IDs before capability verification instead of string-coercing array or other non-string values and deferring rejection to the daemon plan parser.
85
+ - Keep OAuth, admin, and daemon edge throttling bound to the caller network identity even when an attacker adds arbitrary Authorization headers; credential-scoped rate-limit buckets are now reserved for the MCP endpoint that actually authenticates with those credentials.
86
+ - Clarify the local-resource confidentiality boundary: browser/application mutation results do not echo injected resource content, while the destination receives it and later explicit value/source/screenshot observations may expose content the destination now holds.
87
+ - Keep security-audit evidence type-exact: malformed non-string target hashes, principal identifiers, roles, tools, outcomes, and risk categories now fall back to null/unknown evidence instead of being string-coerced into valid-looking audit records.
88
+ - Enforce exact string types for browser pairing grants, challenges, proofs, and credentials, and require an integer pairing port instead of accepting equivalent values through JavaScript coercion.
89
+ - Enforce exact string types for resource-request family and contention identities so malformed persisted/request metadata cannot cross the coordinator contract through JavaScript coercion.
90
+ - Require exact string resource names and resource-file paths in managed-job planning, keeping resource aliases and persisted plan evidence type-exact with the rest of the job schema.
91
+ - Keep MCP subscription and pending-read per-account capacity keys type-exact instead of normalizing non-string runtime values into account identities.
92
+ - Correct multi-account protocol documentation so removed initialization-era requests are described as rejected, not as a compatibility path.
93
+ - Validate relay account/client/family identity again at the in-flight call attribution boundary instead of coercing malformed principals into revocation metadata.
94
+ - Reject unknown in-flight principal kinds instead of misattributing them as local calls, preserving fail-closed revocation and interruption attribution.
95
+ - Persist at most three privacy-bounded managed-job recovery attribution records with fixed interruption reasons and exact timestamps, expose them with the strict recovery-attempt count, and reject coercible or malformed recovery metadata instead of using it to drive automatic relaunch.
96
+ - Require exact current Worker deployment identity types and formats for Worker names, OAuth token-version secrets, historical Worker names, and deployment fingerprints; arrays and other coercible values no longer become valid deployment identity through string conversion.
97
+ - Require runtime package entry, expected package identity, platform selector, and packaged manifest version to have their exact string types before service activation provenance is accepted; malformed manifest arrays can no longer stringify into a valid release version.
98
+ - Rename the fail-closed cached-schema helper from “compatibility” to “guidance” to make explicit that stale host schemas receive refresh instructions but never regain retired execution semantics.
99
+ - Remove the unused asynchronous `auditWarning` request-context mutation; audit persistence failures remain reported through the bounded security-audit failure reporter instead of writing dead state after a response may already have settled.
100
+ - Remove documentation and result-shape accommodation for older browser extensions: the current handshake already requires the exact packaged extension and `computer_observation_v1`, and missing tab-metadata verification evidence now projects as unverified rather than a legacy tri-state.
101
+ - Remove the unwritten legacy `operation-authorization.lock` scan/migration path and parse the current `security-audit.lock` with its actual owner-state contract, restoring live PID attribution in state inventory.
102
+ - Make the current OAuth store contract genuinely current-only: reject one-character legacy account names and clients missing the current DCR registration identity/revision instead of keeping them authorization-readable, and remove stale-registration status from the admin projection.
103
+ - Fail a managed-job step immediately when a still-live spawned child has no verifiable process-start identity; the runner terminates that child instead of allowing an active claim that crash recovery could only classify as ambiguous.
104
+ - Extend private Wrangler toolchain reuse verification to the executable install-script dependencies `esbuild` and `workerd`, and bump the audit marker schema so existing cached installs are revalidated under the stronger contract.
105
+ - Remove the obsolete browser setup note for migrating older local-candidate extension paths; current setup documents only the stable unpacked-extension path.
106
+ - Split managed-job recovery history accounting out of general state validation so recovery attribution has a dedicated bounded module instead of expanding the core status validator beyond its responsibility boundary.
107
+ - Reject multiply linked package files while computing the promotion digest so an alternate hard-link path cannot mutate candidate content behind the verified package path.
108
+ - Centralize Worker-name and OAuth token-version validation in one current identity contract instead of duplicating security-sensitive regular expressions across state, deployment fingerprinting, health checks, and state inventory.
109
+ - Retain temporary Worker-secret files when filename ownership PID/timestamp evidence exceeds safe-integer bounds instead of treating malformed process identity as reclaimable stale state; legacy secret cleanup remains only to remove credential residue safely.
110
+ - Validate tool arguments after tool/role authority gating but before relay operation risk/path authorization, so malformed remote inputs cannot trigger local path classification or other operation-level inspection before schema rejection.
111
+ - Require hardened-npm artifact descriptors and explicit proxy/NO_PROXY overrides to use native string/integer types, preventing coercible arrays or numeric strings from becoming supply-chain identity or network-routing configuration.
112
+ - Extend managed-job resource-output defense-in-depth redaction to exact Base64URL encodings, while documenting that arbitrary transformed or application-specific representations still require `capture_output: "discard"` when credentials may be echoed.
113
+ - Record a privacy-safe error class when a successfully delivered transient-process result cannot clear its stronger pending-recovery marker, preserving the user result while making temporary quota/retention over-protection diagnosable.
114
+ - Keep runtime shutdown on the ownership-checked process-tree supervisor: forced drain now uses zero-additional-grace supervised escalation, preserves already-pending ownership checks, and waits for retained descendant-escalation settlement instead of replacing it with an unverified direct group `SIGKILL`.
33
115
 
34
116
  ## Historical releases
35
117
 
36
- Historical prerelease entries remain available from Git tags and repository release records. From a source checkout, inspect the changelog shipped by a prior release with `git show v<version>:CHANGELOG.md`. This current file intentionally describes only the active candidate and the history location.
118
+ Historical prerelease entries remain available from Git tags and repository history. From a source checkout, inspect the changelog of a prior revision with `git show <revision>:CHANGELOG.md`. This current file intentionally describes only the active candidate and the history location.
package/README.md CHANGED
@@ -25,7 +25,7 @@ Support boundaries are defined in [SUPPORT.md](SUPPORT.md). Contribution, conduc
25
25
  - bounded file, patch, Git, process, diagnostic, application, browser, and managed-job tools;
26
26
  - account roles whose authority is intersected with the connected daemon policy;
27
27
  - root-certified ephemeral daemon sessions, trusted OAuth client binding, refresh-family ownership, and non-escalatable account roles;
28
- - structured, privacy-conscious lifecycle events, a worker-thread-isolated tamper-evident audit chain, and stable error codes;
28
+ - structured, privacy-conscious lifecycle events, a worker-thread-isolated self-verifying audit hash chain with documented same-user rewrite limits, and stable error codes;
29
29
  - control-plane resilience through end-to-end reserved diagnostic capacity, event-loop-aware relay liveness, and explicit draining-process accounting;
30
30
  - fail-closed state, lock, release, package, and supply-chain checks.
31
31
 
@@ -221,7 +221,7 @@ On macOS, authorized remote activity uses a bounded idle-sleep assertion so ordi
221
221
 
222
222
  When `run_process` or `exec_command` returns a child exit code and bounded stdout/stderr, the local process did run. For nested tools such as `ssh`, a remote forced-command usage message or command allowlist is therefore evidence from the target-side authorization layer, not evidence that Machine Bridge blocked process execution. Diagnose and change the narrowest failing layer instead of widening the `full` profile, which already removes Machine Bridge's own shell and path restrictions.
223
223
 
224
- Credentials and files can be registered by alias without returning their contents through MCP:
224
+ Credentials and files can be registered by alias so the injection operation does not place their contents in MCP arguments or echo them in its result:
225
225
 
226
226
  ```sh
227
227
  machine-mcp resource add maintenance-key ~/.ssh/example_maintenance_ed25519
@@ -230,6 +230,7 @@ machine-mcp job submit plan.json
230
230
  ```
231
231
 
232
232
  See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md) for integrity checks, recovery, redaction, cleanup, and residual risks.
233
+ For browser or application injection, the destination receives the content by design and can copy, render, or transmit it; later observations of that destination can therefore expose content it now holds.
233
234
 
234
235
  ## Operations
235
236
 
@@ -243,7 +244,7 @@ machine-mcp workspace show|set|reset
243
244
  machine-mcp idle-sleep show|set MODE
244
245
  machine-mcp service status|install|start|stop|uninstall
245
246
  machine-mcp account list|clients|revoke-client|add|role|enable|disable|rotate-password|remove
246
- machine-mcp browser status|setup|pair|path
247
+ machine-mcp browser status|setup|pair|reset|path
247
248
  machine-mcp resource add|list|check|remove
248
249
  machine-mcp job submit|inspect|list|read|cancel
249
250
  machine-mcp rotate-secrets
package/SECURITY.md CHANGED
@@ -71,7 +71,7 @@ The roles are:
71
71
  - `operator`: workspace-confined mutation and direct process execution;
72
72
  - `owner`: complete bridge authority within the daemon policy ceiling.
73
73
 
74
- No approval ID, refresh token, reconnect, client registration, or legacy lease can expand a role. Out-of-role operations fail with `authorization_denied`.
74
+ No approval ID, refresh token, reconnect, or client registration can expand a role. Out-of-role operations fail with `authorization_denied`.
75
75
 
76
76
  The Worker filters the stable discovery catalog by account role. Discovery is not authorization: each call is separately intersected with the current end-to-end-ready daemon capability ceiling, and the local runtime independently recomputes the role and policy boundary before dispatch. Account disablement, role change, password rotation, account removal, client revocation, token-version rotation, and refresh-family replay invalidate the appropriate credentials.
77
77
 
@@ -189,7 +189,7 @@ After trusted input dispatch begins, an ambiguous failure is reported as unknown
189
189
 
190
190
  Computer Use screenshots share the ordinary MCP result-size boundary. When an image would make an observation result too large, the image is omitted before the snapshot ID is published and pixel-action authority is disabled. When a post-action image would overflow the same boundary, it is removed from the returned/stored post snapshot while the already-established mutation settlement and bounded continuation handle are preserved; the result never turns that dispatched action into a generic retryable size failure. Semantic state may still be returned. The experimental macOS background visual backend is disabled unless explicitly configured and successfully probed; Accessibility, Automation, Screen Recording, and the operating system remain independent enforcement boundaries.
191
191
 
192
- Local resources may be injected without returning their bytes through MCP, but the destination page or application still receives them. Screenshots and page source can themselves contain secrets. In particular, raw serialized HTML may contain hidden bootstrap/session/account/authentication values that are not visible in the rendered page; `browser_get_source` therefore should be used only when raw markup is required, with semantic inspection preferred for routine browser work.
192
+ Local resources may be injected without placing their bytes in the mutating MCP arguments or echoing them in that mutation result, but the destination page or application still receives them. Once delivered, the destination can copy, render, or transmit the value; a later explicit value inspection, screenshot, page source read, or other observation may therefore expose content the destination now holds. In particular, raw serialized HTML may contain hidden bootstrap/session/account/authentication values that are not visible in the rendered page; `browser_get_source` therefore should be used only when raw markup is required, and `browser_inspect_page` should normally keep `include_values=false`.
193
193
 
194
194
  ## Network egress boundaries
195
195
 
package/SUPPORT.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  The supported runtime is the pinned Node.js 26 and npm 12 baseline on current GitHub-hosted Linux, macOS, and Windows environments. Older Node/npm releases and obsolete MCP protocol versions are not compatibility targets.
6
6
 
7
+ Source-release privacy tooling requires Git 2.50 or later. The reachable-history scanner intentionally relies on the machine-parsable NUL-delimited `git rev-list` object format introduced in Git 2.50 and does not carry an older-output compatibility parser.
8
+
7
9
  The project supports the current package version and a direct upgrade from the immediately preceding published version when both use the current local state and policy schemas. See [Upgrading](docs/UPGRADING.md).
8
10
 
9
11
  ## Where to ask
@@ -6,7 +6,7 @@
6
6
  const BROKER_AUTH_REQUEST_VALUE = "machine-bridge-browser-v2";
7
7
 
8
8
  async function extensionProtocol(endpoint, token) {
9
- if (!TOKEN.test(String(token || ""))) throw new Error("browser pairing token is invalid");
9
+ if (typeof token !== "string" || !TOKEN.test(token)) throw new Error("browser pairing token is invalid");
10
10
  const parsed = parseEndpoint(endpoint);
11
11
  if (!parsed) throw new Error("browser broker endpoint is invalid");
12
12
  const clientChallenge = randomBase64Url(24);
@@ -27,8 +27,9 @@
27
27
  }
28
28
 
29
29
  function parseEndpoint(value) {
30
+ if (typeof value !== "string") return null;
30
31
  let parsed;
31
- try { parsed = new URL(String(value || "")); } catch { return null; }
32
+ try { parsed = new URL(value); } catch { return null; }
32
33
  const port = Number(parsed.port);
33
34
  if (parsed.protocol !== "ws:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/extension"
34
35
  || parsed.username || parsed.password || parsed.search || parsed.hash
@@ -40,8 +41,9 @@
40
41
  return parsed ? `http://127.0.0.1:${parsed.port}/pair` : "";
41
42
  }
42
43
  function parsePairingPage(value) {
44
+ if (typeof value !== "string") return null;
43
45
  let parsed;
44
- try { parsed = new URL(String(value || "")); } catch { return null; }
46
+ try { parsed = new URL(value); } catch { return null; }
45
47
  const port = Number(parsed.port);
46
48
  if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/pair"
47
49
  || parsed.username || parsed.password || parsed.search || parsed.hash
@@ -0,0 +1,92 @@
1
+ (() => {
2
+ if (globalThis.__machineBridgeBrowserOperationContract) return;
3
+ const MUTATING_METHODS = new Set([
4
+ "manage_tabs", "point_action", "backend_node_action", "action", "fill_form", "upload_files", "screenshot",
5
+ ]);
6
+ const MUTATION_RESULT_SETTLEMENT_UNKNOWN = "browser mutation may have completed; the action outcome is unknown because its result could not be delivered. Inspect browser state before retrying.";
7
+ function exactFiniteNumber(value) {
8
+ return typeof value === "number" && Number.isFinite(value) ? (Object.is(value, -0) ? 0 : value) : null;
9
+ }
10
+ function exactPositiveFiniteNumber(value) {
11
+ const number = exactFiniteNumber(value);
12
+ return number !== null && number > 0 ? number : null;
13
+ }
14
+ function exactNormalizedCoordinate(value) {
15
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value < 1
16
+ ? (Object.is(value, -0) ? 0 : value) : null;
17
+ }
18
+ function exactPositiveInteger(value) { return Number.isSafeInteger(value) && value > 0 ? value : null; }
19
+ function exactNonNegativeInteger(value) { return Number.isSafeInteger(value) && value >= 0 ? value : null; }
20
+ function exactBoolean(value, label, fallback) {
21
+ if (value === undefined) return fallback;
22
+ if (typeof value !== "boolean") throw new Error(`${label} must be boolean before dispatch`);
23
+ return value;
24
+ }
25
+ function exactBoundedInteger(value, label, fallback, min, max) {
26
+ if (value === undefined) return fallback;
27
+ if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error(`${label} is invalid before dispatch`);
28
+ return value;
29
+ }
30
+ function exactImageFormat(value, label, fallback = "png") {
31
+ if (value === undefined) return fallback;
32
+ if (typeof value !== "string" || !["png", "jpeg"].includes(value)) throw new Error(`${label} is invalid before dispatch`);
33
+ return value;
34
+ }
35
+ function exactOptionalSha256(value, label) {
36
+ if (value === undefined || value === "") return "";
37
+ if (typeof value !== "string" || !/^[A-Fa-f0-9]{64}$/.test(value)) throw new Error(`${label} is invalid before dispatch`);
38
+ return value.toLowerCase();
39
+ }
40
+ function exactOptionalText(value, label, maxLength) {
41
+ if (value === undefined || value === "") return "";
42
+ if (typeof value !== "string" || value.includes("\0") || value.length > maxLength) throw new Error(`${label} is invalid before dispatch`);
43
+ return value;
44
+ }
45
+ function exactOptionalAuthorityString(value, maxLength) {
46
+ if (value === undefined || value === "") return "";
47
+ if (typeof value !== "string" || value.length > maxLength || value.includes("\0")) {
48
+ throw new Error("snapshot authority string is invalid before dispatch");
49
+ }
50
+ return value;
51
+ }
52
+ function requiredSnapshotAuthorityString(value, label, maxLength) {
53
+ const text = exactOptionalAuthorityString(value, maxLength);
54
+ if (!text) throw new Error(`${label} is required before dispatch`);
55
+ return text;
56
+ }
57
+ function requiredSnapshotViewport(value) {
58
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("visual snapshot viewport is required before dispatch");
59
+ const output = {};
60
+ for (const key of ["width", "height", "scale"]) {
61
+ const number = exactPositiveFiniteNumber(value[key]);
62
+ if (number === null) throw new Error(`visual snapshot viewport ${key} is invalid before dispatch`);
63
+ output[key] = number;
64
+ }
65
+ return output;
66
+ }
67
+ function methodMayMutate(method) { return typeof method === "string" && MUTATING_METHODS.has(method); }
68
+ function responsePayload({ id, ok, result, error = "", method = "", maxBytes }) {
69
+ const mutatingSuccess = ok === true && methodMayMutate(method);
70
+ let payload;
71
+ try { payload = JSON.stringify({ type: "response", id, ok, ...(ok ? { result } : { error }) }); }
72
+ catch {
73
+ payload = JSON.stringify({ type: "response", id, ok: false,
74
+ error: mutatingSuccess ? MUTATION_RESULT_SETTLEMENT_UNKNOWN : "browser result could not be serialized" });
75
+ }
76
+ const resultLimit = Number.isSafeInteger(maxBytes) && maxBytes > 0 ? maxBytes : 0;
77
+ if (resultLimit === 0 || new TextEncoder().encode(payload).byteLength > resultLimit) {
78
+ payload = JSON.stringify({ type: "response", id, ok: false,
79
+ error: mutatingSuccess ? MUTATION_RESULT_SETTLEMENT_UNKNOWN : "browser result exceeds maximum size" });
80
+ }
81
+ return payload;
82
+ }
83
+ Object.defineProperty(globalThis, "__machineBridgeBrowserOperationContract", {
84
+ value: Object.freeze({
85
+ exactFiniteNumber, exactPositiveFiniteNumber, exactNormalizedCoordinate, exactPositiveInteger,
86
+ exactNonNegativeInteger, exactBoolean, exactBoundedInteger, exactImageFormat, exactOptionalSha256,
87
+ exactOptionalText, requiredSnapshotAuthorityString, requiredSnapshotViewport,
88
+ exactOptionalAuthorityString, methodMayMutate, responsePayload,
89
+ }),
90
+ configurable: false,
91
+ });
92
+ })();
@@ -2,111 +2,16 @@
2
2
  if (globalThis.__machineBridgeBrowserOperations) return;
3
3
 
4
4
  const MAX_ACCESSIBLE_FRAMES = 64;
5
- const PAGE_AUTOMATION_VERSION = 5;
5
+ const PAGE_AUTOMATION_VERSION = 6;
6
6
  const rendererRequests = new Map();
7
7
  const MAX_RENDERER_REQUESTS = 64;
8
- const MUTATING_METHODS = new Set([
9
- "manage_tabs", "point_action", "backend_node_action", "action", "fill_form", "upload_files", "screenshot",
10
- ]);
11
- const MUTATION_RESULT_SETTLEMENT_UNKNOWN = "browser mutation may have completed; the action outcome is unknown because its result could not be delivered. Inspect browser state before retrying.";
12
-
13
- function exactFiniteNumber(value) {
14
- return typeof value === "number" && Number.isFinite(value) ? (Object.is(value, -0) ? 0 : value) : null;
15
- }
16
-
17
- function exactPositiveFiniteNumber(value) {
18
- const number = exactFiniteNumber(value);
19
- return number !== null && number > 0 ? number : null;
20
- }
21
-
22
- function exactNormalizedCoordinate(value) {
23
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value < 1
24
- ? (Object.is(value, -0) ? 0 : value)
25
- : null;
26
- }
27
-
28
- function exactPositiveInteger(value) {
29
- return Number.isSafeInteger(value) && value > 0 ? value : null;
30
- }
31
-
32
- function exactNonNegativeInteger(value) {
33
- return Number.isSafeInteger(value) && value >= 0 ? value : null;
34
- }
35
-
36
- function exactBoolean(value, label, fallback) {
37
- if (value === undefined) return fallback;
38
- if (typeof value !== "boolean") throw new Error(`${label} must be boolean before dispatch`);
39
- return value;
40
- }
41
-
42
- function exactBoundedInteger(value, label, fallback, min, max) {
43
- if (value === undefined) return fallback;
44
- if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error(`${label} is invalid before dispatch`);
45
- return value;
46
- }
47
-
48
- function exactImageFormat(value, label, fallback = "png") {
49
- if (value === undefined) return fallback;
50
- if (typeof value !== "string" || !["png", "jpeg"].includes(value)) throw new Error(`${label} is invalid before dispatch`);
51
- return value;
52
- }
53
-
54
- function exactOptionalSha256(value, label) {
55
- if (value === undefined || value === "") return "";
56
- if (typeof value !== "string" || !/^[A-Fa-f0-9]{64}$/.test(value)) throw new Error(`${label} is invalid before dispatch`);
57
- return value.toLowerCase();
58
- }
59
-
60
- function exactOptionalText(value, label, maxLength) {
61
- if (value === undefined || value === "") return "";
62
- if (typeof value !== "string" || value.includes("\0") || value.length > maxLength) throw new Error(`${label} is invalid before dispatch`);
63
- return value;
64
- }
65
-
66
- function requiredSnapshotAuthorityString(value, label, maxLength) {
67
- const text = exactOptionalAuthorityString(value, maxLength);
68
- if (!text) throw new Error(`${label} is required before dispatch`);
69
- return text;
70
- }
71
-
72
- function requiredSnapshotViewport(value) {
73
- if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("visual snapshot viewport is required before dispatch");
74
- const output = {};
75
- for (const key of ["width", "height", "scale"]) {
76
- const number = exactPositiveFiniteNumber(value[key]);
77
- if (number === null) throw new Error(`visual snapshot viewport ${key} is invalid before dispatch`);
78
- output[key] = number;
79
- }
80
- return output;
81
- }
82
-
83
- function exactOptionalAuthorityString(value, maxLength) {
84
- if (value === undefined || value === "") return "";
85
- if (typeof value !== "string" || value.length > maxLength || value.includes("\0")) {
86
- throw new Error("snapshot authority string is invalid before dispatch");
87
- }
88
- return value;
89
- }
90
-
91
- function methodMayMutate(method) {
92
- return typeof method === "string" && MUTATING_METHODS.has(method);
93
- }
94
-
95
- function responsePayload({ id, ok, result, error = "", method = "", maxBytes }) {
96
- const mutatingSuccess = ok === true && methodMayMutate(method);
97
- let payload;
98
- try { payload = JSON.stringify({ type: "response", id, ok, ...(ok ? { result } : { error }) }); }
99
- catch {
100
- payload = JSON.stringify({ type: "response", id, ok: false,
101
- error: mutatingSuccess ? MUTATION_RESULT_SETTLEMENT_UNKNOWN : "browser result could not be serialized" });
102
- }
103
- const resultLimit = Number.isSafeInteger(maxBytes) && maxBytes > 0 ? maxBytes : 0;
104
- if (resultLimit === 0 || new TextEncoder().encode(payload).byteLength > resultLimit) {
105
- payload = JSON.stringify({ type: "response", id, ok: false,
106
- error: mutatingSuccess ? MUTATION_RESULT_SETTLEMENT_UNKNOWN : "browser result exceeds maximum size" });
107
- }
108
- return payload;
109
- }
8
+ const {
9
+ exactFiniteNumber, exactPositiveFiniteNumber, exactNormalizedCoordinate, exactPositiveInteger,
10
+ exactNonNegativeInteger, exactBoolean, exactBoundedInteger, exactImageFormat, exactOptionalSha256,
11
+ exactOptionalText, requiredSnapshotAuthorityString, requiredSnapshotViewport,
12
+ exactOptionalAuthorityString, methodMayMutate, responsePayload,
13
+ } = globalThis.__machineBridgeBrowserOperationContract || {};
14
+ if (typeof responsePayload !== "function") throw new Error("browser operation contract is unavailable");
110
15
 
111
16
  async function dispatch(method, params, state) {
112
17
  if (method === "list_tabs") return listTabs(params);
@@ -4,7 +4,6 @@
4
4
  "version": "3.0.0",
5
5
  "description": "Connects the current Chromium browser profile to the local Machine Bridge runtime for user-authorized automation.",
6
6
  "permissions": [
7
- "tabs",
8
7
  "debugger",
9
8
  "scripting",
10
9
  "storage",
@@ -30,6 +29,6 @@
30
29
  "action": {
31
30
  "default_title": "Machine Bridge Browser"
32
31
  },
33
- "version_name": "3.0.0-beta.198",
32
+ "version_name": "3.0.0-beta.199",
34
33
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxryYkpZhq8+VAQLHcGS9BAHQcyKX8RHGIpIwvtIVRU/rcOcE0bNdnM0aZJ/h6xWQsGDHlhvjT2+1aJaAn/9k8473BRWajzVXld961CdHYVFVHoce2hHiSJ0xydWrHMMZhAm0mN0UzjEpgZ0tMw209efcZHIvSwuxhteZMRy4kyiVjwFlOf5oXFCxRuCJnPj3AK9CmCf4XgEBuPIJ0TZmjGHOOdBvJmbCNnAWXYEo5/mf7MfCGhV4IJ1hNuhpoNQfOFKMUcw9/v/IpT62XpfXdGYTfGYCmCjC+gntK1spbkr2P4/2+sYMQtLpse71mpSNGXfcf3abU55Vpn+gncSxRQIDAQAB"
35
34
  }
@@ -1,5 +1,5 @@
1
1
  (() => {
2
- const PAGE_AUTOMATION_VERSION = 5;
2
+ const PAGE_AUTOMATION_VERSION = 6;
3
3
  if (globalThis.__machineBridgePageAutomation?.version === PAGE_AUTOMATION_VERSION) return;
4
4
 
5
5
  const INTERACTIVE_SELECTOR = "a,button,input,select,textarea,[role],[contenteditable]:not([contenteditable='false']),summary";
@@ -19,7 +19,7 @@
19
19
  const SNAPSHOT_IDENTITY_FIELDS = new Set([
20
20
  "tag", "type", "role", "name", "id", "field_name", "label", "placeholder", "href", "sensitive", "in_shadow_dom",
21
21
  ]);
22
- const DOCUMENT_EPOCH = `doc_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 14)}`;
22
+ const DOCUMENT_EPOCH = `doc_${Date.now().toString(36)}_${crypto.randomUUID()}`;
23
23
  const elementRefs = new WeakMap();
24
24
  const refElements = new Map();
25
25
  let nextRef = 1;
@@ -5,18 +5,17 @@
5
5
  async function bootstrapPairing(port, grant, now = Date.now()) {
6
6
  const auth = globalThis.__machineBridgeBrokerAuth?.internal;
7
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)) {
8
+ const match = typeof grant === "string" ? GRANT.exec(grant) : null;
9
+ if (!Number.isSafeInteger(port) || port < 1024 || port > 65535 || !match
10
+ || typeof now !== "number" || !Number.isFinite(now)) {
12
11
  throw new Error("browser pairing bootstrap is invalid");
13
12
  }
14
13
  const expiresAt = Number(match[1]);
15
- if (expiresAt < current || expiresAt - current > PAIRING_GRANT_TTL_MS) throw new Error("browser pairing bootstrap expired");
14
+ if (expiresAt < now || expiresAt - now > PAIRING_GRANT_TTL_MS) throw new Error("browser pairing bootstrap expired");
16
15
  const grantId = `${match[1]}.${match[2]}`;
17
16
  const secret = match[3];
18
17
  const clientChallenge = auth.randomBase64Url(24);
19
- const authUrl = new URL(`http://127.0.0.1:${normalizedPort}/pair-auth`);
18
+ const authUrl = new URL(`http://127.0.0.1:${port}/pair-auth`);
20
19
  authUrl.searchParams.set("grant", grantId);
21
20
  authUrl.searchParams.set("challenge", clientChallenge);
22
21
  const initProof = await auth.hmac(secret, `machine-bridge-browser-pair-init-v2\0${grantId}\0${clientChallenge}`);
@@ -35,7 +34,7 @@
35
34
  if (second.status !== 204) throw new Error("browser pairing broker authentication failed");
36
35
  const token = String(second.headers.get("x-machine-bridge-extension-token") || "");
37
36
  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 };
37
+ return { endpoint: `ws://127.0.0.1:${port}/extension`, token };
39
38
  }
40
39
 
41
40
  globalThis.__machineBridgePairingBootstrap = Object.freeze({ bootstrapPairing });