machine-bridge-mcp 3.0.0-beta.21 → 3.0.0-beta.26

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 (102) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/CONTRIBUTING.md +3 -3
  3. package/GOVERNANCE.md +2 -2
  4. package/README.md +24 -6
  5. package/browser-extension/manifest.json +1 -1
  6. package/docs/AGENT_CONTEXT.md +10 -7
  7. package/docs/ARCHITECTURE.md +35 -22
  8. package/docs/AUDIT.md +85 -1
  9. package/docs/CLIENTS.md +6 -2
  10. package/docs/ENGINEERING.md +31 -9
  11. package/docs/LOCAL_AUTOMATION.md +4 -2
  12. package/docs/LOGGING.md +8 -8
  13. package/docs/OPERATIONS.md +43 -17
  14. package/docs/PRIVACY.md +18 -4
  15. package/docs/PROJECT_STANDARDS.md +2 -2
  16. package/docs/RELEASING.md +35 -11
  17. package/docs/TESTING.md +36 -16
  18. package/docs/THREAT_MODEL.md +20 -5
  19. package/docs/TOOL_REFERENCE.md +18 -12
  20. package/docs/UPGRADING.md +32 -0
  21. package/package.json +15 -6
  22. package/scripts/check-plan.mjs +8 -0
  23. package/scripts/coverage-check.mjs +30 -1
  24. package/scripts/foreground-daemon-recovery.mjs +88 -0
  25. package/scripts/github-release.mjs +22 -16
  26. package/scripts/install-published-prerelease.mjs +7 -7
  27. package/scripts/official-mcp-conformance.mjs +243 -0
  28. package/scripts/persistent-activation-process.mjs +36 -0
  29. package/scripts/release-candidate-manifest.mjs +12 -0
  30. package/scripts/release-publication-guard.mjs +65 -0
  31. package/scripts/release-state.mjs +1 -1
  32. package/scripts/sbom-check.mjs +99 -0
  33. package/scripts/start-release-candidate.mjs +39 -13
  34. package/src/local/agent-context-projection.mjs +26 -7
  35. package/src/local/agent-context.mjs +25 -4
  36. package/src/local/autostart-log-maintenance.mjs +36 -0
  37. package/src/local/capability-observer.mjs +5 -0
  38. package/src/local/child-process-settlement.mjs +103 -0
  39. package/src/local/cli-activate.mjs +42 -5
  40. package/src/local/cli-service.mjs +55 -5
  41. package/src/local/cli.mjs +59 -10
  42. package/src/local/daemon-process.mjs +24 -3
  43. package/src/local/delegated-process-sandbox.mjs +1 -0
  44. package/src/local/execution-routing.mjs +231 -0
  45. package/src/local/git-service.mjs +3 -1
  46. package/src/local/job-runner.mjs +55 -19
  47. package/src/local/macos-trust-broker.mjs +7 -0
  48. package/src/local/managed-job-runner-claim.mjs +54 -0
  49. package/src/local/managed-job-runner.mjs +13 -2
  50. package/src/local/process-execution.mjs +2 -2
  51. package/src/local/process-identity.mjs +11 -0
  52. package/src/local/process-tree-ownership-types.d.ts +37 -0
  53. package/src/local/process-tree-ownership.mjs +49 -41
  54. package/src/local/process-tree.mjs +1 -1
  55. package/src/local/relay-call-recovery.mjs +40 -21
  56. package/src/local/runtime-activation.mjs +357 -38
  57. package/src/local/runtime-capabilities.mjs +22 -6
  58. package/src/local/runtime-diagnostics.mjs +9 -2
  59. package/src/local/runtime.mjs +18 -4
  60. package/src/local/service-convergence.mjs +33 -0
  61. package/src/local/service-owner.mjs +147 -0
  62. package/src/local/service-restart-handoff.mjs +22 -8
  63. package/src/local/service-runtime.mjs +145 -0
  64. package/src/local/service.mjs +143 -25
  65. package/src/local/state.mjs +104 -7
  66. package/src/local/stdio.mjs +139 -45
  67. package/src/local/system-network-route.mjs +76 -0
  68. package/src/local/tool-executor.mjs +24 -6
  69. package/src/local/tools.mjs +6 -5
  70. package/src/local/windows-service-convergence.mjs +49 -0
  71. package/src/local/windows-service.mjs +30 -53
  72. package/src/shared/mcp-protocol.d.mts +27 -0
  73. package/src/shared/mcp-protocol.mjs +256 -0
  74. package/src/shared/mcp-subscriptions.d.mts +4 -0
  75. package/src/shared/mcp-subscriptions.mjs +59 -0
  76. package/src/shared/relay-contract.json +1 -0
  77. package/src/shared/result-projection.d.mts +2 -1
  78. package/src/shared/result-projection.mjs +13 -2
  79. package/src/shared/server-metadata.json +11 -4
  80. package/src/shared/tool-argument-validation.d.mts +17 -0
  81. package/src/shared/tool-argument-validation.mjs +325 -0
  82. package/src/shared/tool-catalog.json +18 -12
  83. package/src/worker/durable-stream-calls.ts +12 -24
  84. package/src/worker/http.ts +36 -2
  85. package/src/worker/index.ts +181 -165
  86. package/src/worker/mcp-http-contract.ts +276 -0
  87. package/src/worker/mcp-jsonrpc.ts +12 -6
  88. package/src/worker/mcp-legacy-dispatch.ts +104 -0
  89. package/src/worker/mcp-modern-controller.ts +199 -0
  90. package/src/worker/mcp-modern-proxy.ts +126 -0
  91. package/src/worker/mcp-modern-stream.ts +71 -0
  92. package/src/worker/mcp-session.ts +12 -3
  93. package/src/worker/mcp-stream-proxy-contract.ts +67 -0
  94. package/src/worker/mcp-stream-proxy.ts +17 -60
  95. package/src/worker/mcp-tool-call-input.ts +23 -0
  96. package/src/worker/tool-catalog.ts +29 -1
  97. package/src/worker/tool-timeout.ts +53 -12
  98. package/src/worker/worker-mcp-config.ts +23 -0
  99. package/src/worker/worker-metadata.ts +10 -1
  100. package/src/worker/worker-runtime-config.ts +19 -0
  101. package/src/worker/worker-static-routes.ts +7 -2
  102. package/tsconfig.local.json +7 -1
@@ -33,7 +33,7 @@ A canonical workspace receives an independent profile, Worker name, secret set,
33
33
  - `process-contract.mjs` owns argv shape/size validation, `process-tree.mjs` owns cross-platform tree termination, `process-execution.mjs` and `process-sessions.mjs` own one-shot and interactive execution, and `process-tracker.mjs` owns runtime process accounting;
34
34
  - `runtime-reporting.mjs` builds privacy-aware runtime and project snapshots;
35
35
  - `runtime-diagnostics.mjs` owns fixed local probes and their stable interpretation;
36
- - `runtime-capabilities.mjs` composes agent, application, and browser capability results;
36
+ - `runtime-capabilities.mjs` composes agent, application, browser, and effective-policy-filtered routing results, while `execution-routing.mjs` owns bounded set-level route scoring, ambiguity, fallbacks, and advisory tool projection;
37
37
  - `runtime-tool-handlers.mjs` owns catalog-to-handler registration;
38
38
  - `runtime-relay.mjs` owns relay construction and inbound envelope normalization, while `relay-call-recovery.mjs` owns the bounded disconnect grace, result queue, authoritative resumed-call reconciliation, replay, and expiry cleanup;
39
39
  - `runtime-paths.mjs` owns runtime-directory creation, containment checks, and error-path redaction;
@@ -46,13 +46,19 @@ Architecture tests cap the orchestration module and each extracted service indep
46
46
 
47
47
  `daemon-process.mjs` owns workspace-daemon inspection and takeover. It distinguishes platform service state from the lock-owning Node process, validates PID and process-start identity, canonicalizes workspace/state paths before comparison, parses bounded process command lines without executing them, and accepts lock-backed `--daemon-only` recovery processes that omit repeated path flags. Stop/takeover sends `SIGTERM` only to a verified same-workspace service daemon. If it remains alive after the grace period, the code revalidates PID, process-start identity, command line, entrypoint, daemon mode, workspace, and state root before sending `SIGKILL`; a foreground, replaced-PID, or otherwise unverifiable process remains untouched. CLI orchestration never treats a missing launchd/systemd job as proof that the process exited.
48
48
 
49
+ `service-owner.mjs` is the machine-global identity ledger for the one launchd/systemd/Task Scheduler definition. Its owner-only record binds the canonical workspace, state root, exact runtime entrypoint, and package version. Installation is a pending-to-committed transaction: once provider mutation begins, failure remains pending because the code cannot prove that the external service manager made no partial change. `service-runtime.mjs` loads only a committed owner, verifies provider state, starts or restarts the provider, and waits for the matching daemon lock to publish its token-protected startup-readiness checkpoint. That checkpoint is written once, after device authentication, relay probing, and `ready_ack`; PID stability or a provider `active` flag is not equivalent evidence. Missing, corrupt, pending, mismatched, or unready ownership fails closed.
50
+
51
+ All machine-global service writers share a fixed per-user machine-service lock independent of workspace and custom state roots. The lock and service-owner ledger live in a dedicated control root (`machine-bridge-mcp-control`), while ordinary workspace/profile state uses `machine-bridge-mcp`; XDG and Windows APPDATA preserve the same sibling separation. The control root is never a default state root or candidate-runtime root. Paths that also need a workspace startup lock acquire machine-service first and startup second. Foreground startup releases the machine lock after provider takeover and daemon ownership are established, before entering the long-lived runtime; activation retains it through candidate verification, definition commit, service launch, and background readiness. Service-spawned `--daemon-only` children do not reacquire the parent transaction lock. This ordering prevents cross-workspace service-definition races, state/control namespace collision, and machine/startup lock cycles.
52
+
49
53
  ### Agent context and capability resolver
50
54
 
51
55
  `AgentContextManager` discovers the nearest Git/workspace scope, applies the user configuration and hierarchical `.machine-bridge/agent.json` files, selects built-in/user/root-to-target instructions, discovers bounded filesystem skills, and resolves registered commands. `agent-context-projection.mjs` owns capability fingerprints, privacy-aware public projections, bounded skill summaries, command rendering, and effective-instruction rendering. `agent-skill-discovery.mjs` owns bounded skill-root traversal, symlink containment, metadata parsing, warnings, and file inventory; `agent-text-file.mjs` owns no-follow bounded UTF-8 reads. Filesystem/config discovery no longer maintains those output and skill-scanning mechanics inline. `agent-contract.mjs` is the strict checked-JavaScript boundary for configuration shape, registered-command normalization, encoded-size limits, and configured-path containment; the manager does not maintain a second parser. `project-package.mjs` owns no-follow package metadata parsing, package-manager selection (including fail-closed conflicting-lockfile handling), script-name normalization, bounded workflow-intent aliases, and automatic `package.*` command construction so instruction rendering and command execution do not duplicate package parsing. `default-instructions.mjs` supplies a versioned in-package working-agreement block and derives a small virtual project-context block from root filenames and bounded metadata. It reads package script names but not bodies, does not inspect dependency values or source contents, executes nothing, and writes no user/repository files. A global `model_instructions_file` is a separate user-designated session source and cannot be overridden by a project.
52
56
 
53
- `session_bootstrap` is requested during both stdio and remote MCP initialization. The Worker delegates this read to the connected daemon with a short bounded timeout; failure falls back to static server instructions rather than blocking initialization indefinitely. `resolve_task_capabilities` performs a fresh deterministic scan, rebuilds automatic project facts, and ranks skill/command metadata for the current task. Application and browser capability metadata is added by `LocalRuntime`; installed application inventory uses a short bounded cache. `CapabilityObserver` records only counts, timestamps, source flags, selected metadata, match counts, recommended tool names, and a runtime-keyed task fingerprint so operators can verify routing without creating a task-content log.
57
+ `session_bootstrap` is requested during both stdio and remote MCP initialization. The Worker delegates this read to the connected daemon with a short bounded timeout; failure falls back to static server instructions rather than blocking initialization indefinitely. `resolve_task_capabilities` performs a fresh deterministic scan, rebuilds automatic project facts, ranks skills, commands, and policy-visible tools, then scores compatible execution surfaces as sets rather than independent names. Registered commands, Bash/direct argv, process sessions, managed jobs, files/Git, browser, applications, protected resources, and diagnostics remain distinct routes with explicit fallbacks. The routing envelope is schema-versioned; scores are deterministic relative ranks within one response, not probabilities or stable cross-version values. Routing is advisory and never narrows the effective policy; `exec_command` remains available as the general shell escape hatch.
58
+
59
+ Application and browser discovery uses the request's effective account/daemon policy intersection, not the daemon ceiling. A restricted account therefore cannot receive local application inventory or browser/shell recommendations it could not invoke. Installed application inventory uses a short bounded cache. `CapabilityObserver` records only counts, timestamps, source flags, match counts, recommended tool names, primary route, ambiguity class, score gap, and a runtime-keyed task fingerprint; it stores no task text.
54
60
 
55
- The MCP catalog remains static: local skills and commands do not become dynamically named tools. This avoids stale host catalog caches and keeps Worker/stdio schema parity. Progressive disclosure separates discovery, instruction loading, and execution authority. A refresh fingerprint is descriptive rather than a cache-validity guarantee.
61
+ The MCP catalog remains static: local skills and commands do not become dynamically named tools. This avoids stale host catalog caches and keeps Worker/stdio schema parity. Progressive disclosure separates discovery, instruction loading, routing advice, and execution authority. The refresh fingerprint binds the target/scope, configuration paths, instruction source/precedence/content identity, skill source identity, and complete registered-command definition. Returning it as `known_refresh_fingerprint` permits the server to omit unchanged static context while still rescanning and recomputing task-specific matches. It is not authorization, a conversation identifier, or permission to reuse stale tool results.
56
62
 
57
63
  See [Session instructions, skills, commands, and capability discovery](AGENT_CONTEXT.md).
58
64
 
@@ -113,12 +119,19 @@ Public `/healthz`, `/`, discovery metadata, CORS preflight, and unknown-path 404
113
119
  - policy/tool metadata attached to the active socket;
114
120
  - a bounded in-memory map for JSON-only daemon calls whose initiating request still owns the terminal Promise;
115
121
  - one short FIFO admission gate that computes the combined 32-call ceiling across the in-memory and persistent paths;
116
- - a bounded persistent index for streamed daemon-call ownership, opaque connection generation, request correlation, result-transform metadata, and monotonic operation/reconnect deadlines;
117
- - bounded resumable MCP delivery metadata and terminal responses for recently disconnected SSE clients.
122
+ - a bounded in-memory pending-call index for active modern request-scoped HTTP streams; no modern terminal Promise or result is retained after the initiating stream event;
123
+ - a bounded persistent index for legacy streamed daemon-call ownership, opaque connection generation, request correlation, result-transform metadata, and monotonic operation/reconnect deadlines;
124
+ - bounded legacy resumable MCP delivery metadata and terminal responses for recently disconnected legacy SSE clients.
125
+
126
+ `BridgeRoom` owns stateful routing, MCP authorization/dispatch, daemon WebSocket lifecycle, cancellation, and composition of the extracted state machines. `worker-entry.ts` owns outer-Worker static routing, stateful admission, protocol-era-aware SSE proxy selection, and privacy-safe gateway failures; `worker-static-routes.ts` and `worker-metadata.ts` own stateless public responses; `worker-edge-guard.ts` owns the burst guard and quota classification. Both outer and Durable Object `/mcp` boundaries validate the actual Origin. `mcp-http-contract.ts` owns modern per-request metadata, strict dual-media `Accept`, and mirrored-header validation, including `MCP-Protocol-Version`, `Mcp-Method`, `Mcp-Name`, and schema-declared `Mcp-Param-*`. `mcp-tool-call-input.ts` is the shared role-visible name/raw-argument/schema gate used by modern and legacy dispatch before side effects. `mcp-modern-controller.ts` owns modern request dispatch, while `mcp-modern-stream.ts` owns direct request/subscription SSE framing without event IDs or replay. `mcp-modern-proxy.ts` forwards exactly one modern Durable Object response, emits bounded keepalive comments, releases the internal reader on every terminal path, and maps public stream closure to a credential-free stream-scoped private cancel control; it has no prepare/subscribe phase or result registry. `mcp-stream-proxy.ts` routes modern direct streams and translates only legacy recovery descriptors. Every caller-supplied internal control header is stripped at the public boundary.
127
+
128
+ The legacy adapter remains isolated behind the same entrypoint. `mcp-stream-subscription.ts`, `mcp-stream-channel.ts`, `mcp-resumption-http.ts`, `mcp-resumption.ts`, `mcp-resumption-records.ts`, `mcp-resumption-index.ts`, `mcp-pending-call-store.ts`, `mcp-pending-call-records.ts`, and `durable-stream-calls.ts` own the MCP `2025-11-25` signed-session, persistent-call, recovery-GET, and `Last-Event-ID` contract. `runtime-alarm.ts` and `runtime-alarm-storage.ts` own earliest-deadline projection and coalesced alarm writes. `daemon-sockets.ts` owns socket role transitions, while `daemon-socket-attachment.ts` owns bounded attachment decoding. `mcp-jsonrpc.ts` owns JSON-RPC shape validation and result/error/tool-result projection. `mcp-session.ts` is legacy-only. `websocket-protocol.ts` owns record validation plus best-effort send/close/rejection helpers. `OAuthController` owns OAuth-store pruning, registration throttling, authorization submission, account-admin routing, token exchange, access-token verification, and the serialization queue for OAuth mutations. Worker-internal TypeScript imports use explicit `.ts` specifiers and JSON import attributes, so the same modules are directly executable under the pinned Node runtime and bundled by Wrangler.
129
+
130
+ Modern MCP `2026-07-28` requests are independent. The Worker validates Origin, authenticates the direct request, checks body metadata and mirrored HTTP headers, intersects the tool with the account-visible catalog, and validates raw arguments before dispatch. OAuth token plus JSON-RPC request ID is deliberately not a global request key: two clients sharing one token may reuse the same ID concurrently. A modern streamed call remains owned by its initiating response stream and the ordinary bounded pending-call index. The outer response has no SSE event ID; public request abort, response-body cancellation, or failed keepalive delivery sends one random internal stream capability that removes the pending call and aborts the daemon operation. Public requests cannot supply that capability because internal headers are stripped, and the cancel control is processed before OAuth without forwarding Authorization or DPoP. There is no modern descriptor subscription, cross-event terminal Promise, or persisted replay result.
118
131
 
119
- `BridgeRoom` owns stateful routing, MCP authorization/dispatch, daemon WebSocket lifecycle, cancellation, and composition of the extracted state machines. `worker-entry.ts` owns outer-Worker static routing, stateful admission, SSE proxy selection, and privacy-safe gateway failures; `worker-static-routes.ts` and `worker-metadata.ts` own stateless public responses; `worker-edge-guard.ts` owns the burst guard and quota classification. `mcp-stream-proxy.ts` owns public SSE adaptation, while `mcp-stream-subscription.ts` owns bounded terminal-subscription retries and payload validation. `mcp-stream-channel.ts` owns Durable Object subscriber registration, single-subscriber replacement, and hibernation-safe terminal push. `mcp-access.ts` owns shared Bearer/DPoP authorization for POST and recovery GET. `mcp-resumption-http.ts` owns recovery routing and signed session/protocol binding and returns descriptors rather than a long-lived response. `mcp-resumption.ts` owns stream admission, immediate pending/terminal polls, expiry, replay, and guarded terminal writes; `mcp-resumption-records.ts` and `mcp-resumption-index.ts` own record validation, compact indexing, terminal-message bounds, integrity metadata, pruning, and eviction. `mcp-pending-call-store.ts` and `mcp-pending-call-records.ts` own persistent streamed-call identity, capacity, request-key uniqueness, deadlines, detach/rebind, and connection-generation checks. `durable-stream-calls.ts` owns streamed-call cancellation, timeout, settlement, and combined observability; `runtime-alarm.ts` and `runtime-alarm-storage.ts` own earliest-deadline projection and coalesced alarm writes. `daemon-sockets.ts` owns socket role transitions, while `daemon-socket-attachment.ts` owns bounded attachment decoding. `mcp-stream.ts` owns SSE framing and heartbeats. `mcp-jsonrpc.ts` owns JSON-RPC shape validation, result/error framing, MCP tool-result projection, session-instruction bounds, and protocol-header validation. `websocket-protocol.ts` owns record validation plus best-effort send/close/rejection helpers. `OAuthController` owns OAuth-store pruning, registration throttling, authorization submission, account-admin routing, token exchange, access-token verification, and the serialization queue for OAuth mutations. Worker-internal TypeScript imports use explicit `.ts` specifiers and JSON import attributes, so the same modules are directly executable under the pinned Node runtime and bundled by Wrangler.
132
+ Legacy MCP `2025-11-25` requests use the compatibility adapter. It issues a signed `Mcp-Session-Id`, scopes duplicate detection and explicit cancellation to token + session + typed JSON-RPC ID, and may persist bounded recovery state before dispatch. The outer Worker emits legacy sequence-zero/sequence-one event IDs; authenticated `GET /mcp` with the original session and `Last-Event-ID` may recover the terminal response. At most 64 legacy records and 1.5 MiB of terminal JSON per record are retained for two minutes. A Durable Object restart may rediscover a persisted legacy call; this recovery machinery is never consulted by the modern dispatcher.
120
133
 
121
- The Worker verifies OAuth, validates MCP envelopes and optional protocol headers, converts `tools/call` into WebSocket messages, correlates explicit cancellation by access-token hash, signed MCP session, and JSON-RPC ID, and formats text/structured/image results. An HTTP/SSE disconnect is transport disposal, not MCP cancellation. For streamed daemon tools, `BridgeRoom` first commits the recovery record, then transactionally attaches the durable call ID, daemon-process identity, per-WebSocket connection generation, client request key, operation deadline, and optional bounded result transform before sending the daemon envelope and returning an internal descriptor. The later WebSocket result, explicit cancellation, operation timeout, send failure, or reconnect-grace expiry converges through one guarded terminal write. A Durable Object restart can therefore rediscover the call and keep it pending; only a pending stream record with no durable call owner becomes the restart-ambiguity result. JSON-only calls retain the ordinary in-event Promise path. The outer Worker sends `stream:0`, heartbeats, and `stream:1`; the Durable Object accepts one hibernatable subscriber WebSocket per stream and pushes the terminal JSON-RPC envelope once. Authenticated `GET /mcp` with `Last-Event-ID` resumes only the original OAuth-token/MCP-session stream; POST always creates new work. Public requests cannot select internal descriptor/subscribe modes because the outer boundary removes those headers before forwarding. At most 64 records and 1.5 MiB of terminal JSON per record are retained for two minutes. The recovery store tracks stream and call ownership, not live Promises; a transient terminal map is used only when persistence fails. It has no local filesystem or process API.
134
+ The shared tool catalog is executable protocol data rather than documentation only. `tool-argument-validation.mjs` compiles the supported JSON Schema 2020-12 subset at process/module initialization, rejects unsupported dialects or keywords instead of silently weakening them, refuses automatic network `$ref` dereference, and bounds schema depth, node count, pattern length, issue count, and total runtime validation steps. Array elements and each own object property consume work; object traversal does not allocate an unbounded key array before checking the budget. The open JSON portions of modern metadata, capabilities/extensions, and subscription filters use a separate 4,096-node/32-level/bounded-key structural walk, and resource subscription lists are count/length bounded. Worker validation prevents invalid remote calls from reaching the daemon or legacy durable state; local validation remains a second boundary for stdio, relay, and direct runtime entrypoints. Validation diagnostics contain only JSON Pointer instance path, keyword, and constraint text—never the rejected value or an unbounded caller-supplied identifier.
122
135
 
123
136
 
124
137
  ### Daemon device authentication
@@ -171,22 +184,22 @@ Remote OAuth binds each code, access token, and refresh token to a named Machine
171
184
  4. The user verifies client name and redirect URI and enters a Machine Bridge account name and password.
172
185
  5. The Worker creates a five-minute code bound to client, redirect, resource, normalized scope, and PKCE challenge.
173
186
  6. A valid verifier exchanges the one-time code for an expiring access token and refresh token; only their hashes are stored. A refresh request is bound to the original public client, account, scope, resource, account version/role, deployment token version, and optional DPoP key. Rotation derives one replacement pair from the consumed token and the private deployment token version, then permits at most two identity-equivalent responses with that exact pair during a 30-second concurrency window; over-budget retries are throttled, and replay after the window revokes the family.
174
- 7. The MCP client initializes against the sole current protocol version; an obsolete client must upgrade rather than enter a legacy execution path. The Worker returns a stateless HMAC-bound `MCP-Session-Id`, and later request/cancellation correlation is scoped by OAuth token, MCP session, JSON-RPC id type, and id value. Two clients may therefore reuse the same JSON-RPC id concurrently without collision. Sessionless POSTs remain independent and are not inserted into a token-global cancellation index. When the daemon advertises `session_bootstrap`, the Worker requests bounded local instructions and appends them to the initialization result; failure degrades to static instructions and increments the bounded `session_bootstrap_failed` observability counter.
175
- 8. A new daemon first authenticates as a bounded `probing` socket. The Worker sends a random `relay_probe`; the local runtime returns it through the normal session-bound result-delivery path; only the matching result produces `ready_ack`, promotion to the active daemon, and safe replacement of an incumbent connection.
187
+ 7. A modern MCP client sends `server/discover` or another request with MCP `2026-07-28` metadata on every call. It receives no protocol session; request and cancellation ownership are scoped to that individual request or response stream, so separate clients may reuse the same typed JSON-RPC ID even with one OAuth token. A legacy MCP `2025-11-25` client selects the compatibility adapter by opening with `initialize`; only that path receives a signed `Mcp-Session-Id` and session-scoped cancellation/recovery. Modern discovery returns static bounded guidance, while `session_bootstrap` explicitly refreshes local instructions and project context. Legacy initialization may request the same bounded bootstrap material; failure degrades to static instructions and increments the bounded `session_bootstrap_failed` counter.
188
+ 8. A new daemon first authenticates as a bounded `probing` socket. The Worker sends a random `relay_probe` over the daemon control plane; the local runtime returns the matching control result, and only that result produces `ready_ack`, promotion to the active daemon, and safe replacement of an incumbent connection. This readiness exchange is below MCP and has no protocol-session identity.
176
189
  9. `tools/list` is a stable package-and-account-role discovery catalog and declares `listChanged: false`; a brief relay interruption does not mutate it. `server_info.authorization.effective_tools` is the live daemon/account intersection and is the authority diagnostic.
177
- 10. `tools/call` receives a random relay call ID and is bound to the daemon process's ephemeral instance identifier, a random per-WebSocket connection generation, and the authenticated client request key. When the client accepts `text/event-stream`, `BridgeRoom` commits recovery state plus durable call ownership and deadlines before sending the daemon envelope, then returns a bounded descriptor immediately; the outer Worker owns the SSE priming frame, keepalives, and one internal terminal subscription. No unresolved terminal Promise, JavaScript timer, or Durable Object `waitUntil` owns the streamed dispatch. JSON-only clients retain the single terminal response.
178
- 11. The runtime validates policy and arguments, executes the tool, and returns a bounded result. Closing or losing the HTTP response stream only makes that stream unwritable; it is not an MCP cancellation and does not remove the pending request.
179
- 12. If the socket remains ready, the Durable Object accepts the result only from that connection generation. If it drops, the Worker durably detaches the streamed call for at most two minutes and accepts completion only after a replacement socket with the same daemon-process identifier has passed the end-to-end readiness probe and atomically acquired a new connection generation. A delayed result or close event from the old socket cannot settle or detach the rebound call. The local runtime preserves the operation and queues a completion over the same shared interval. The Worker pauses the record's remaining normal deadline while detached and resumes it after same-instance rebinding, so connected calls are not granted an unconditional recovery extension.
180
- 13. Only a matching session-scoped `notifications/cancelled` request removes the pending indexes and sends best-effort cancellation to a connected daemon. Local completion that races with explicit cancellation is discarded. On every readiness handover, the Worker first sends an authoritative bounded `resume_calls` set; the runtime cancels active calls and queued results absent from that set before accepting `ready_ack`. A request explicitly cancelled while disconnected therefore cannot be revived by a fast reconnect.
181
- 14. If same-instance readiness does not return before the grace deadline, the Worker rejects the detached request and the local runtime cancels ordinary calls, terminates their process trees, and discards queued results. A newly started daemon has a different instance identifier and cannot inherit prior calls.
182
- 15. `start_job` is different: after durable acceptance, the detached runner is no longer bound to the relay call or socket. Later cancellation uses `cancel_job` or the local CLI.
190
+ 10. A modern `tools/call` receives a random relay call ID only after role-visible name and raw arguments pass the shared schema gate. A JSON response remains in the initiating Durable Object event. If the Worker selects SSE, the outer Worker assigns a random private stream capability, makes one authenticated direct Durable Object request, and forwards the non-resumable response stream. If the public stream closes, a second credential-free internal request presents only that capability; it is handled before OAuth/DPoP and can cancel only the matching active call. No modern descriptor, terminal-result registry, recovery GET, event ID, or `Last-Event-ID` state exists. A legacy streamed call validates first, then binds OAuth token + signed session + typed JSON-RPC ID, commits bounded durable call/recovery state before daemon dispatch, and returns a descriptor that the outer Worker turns into the sequence-zero/sequence-one resumable stream.
191
+ 11. The local runtime validates policy and arguments, executes the tool, and produces a bounded JSON-serializable result. It retains the daemon-to-Worker terminal envelope after WebSocket queueing and replays it until the Worker returns `tool_result_ack`; queue acceptance is not durable delivery. This relay acknowledgement contract is independent of the public MCP era. Closing a modern HTTP response cancels its pending call through the private stream control. Closing a legacy response leaves the bounded operation recoverable; only legacy `notifications/cancelled`, a deadline, or reconnect-grace expiry cancels it.
192
+ 12. The Durable Object accepts a result only from the registered WebSocket generation. A transient modern call settles its in-memory pending record and current HTTP response; a legacy streamed call settles the generation-checked durable terminal store. If the daemon socket drops, both call classes may detach below the MCP transport for the bounded same-daemon reconnect interval. The same daemon-process identifier may reclaim them only after a fresh readiness probe; a new daemon process cannot. A stale socket result or close event cannot settle or detach a rebound call. Modern public HTTP recovery is still impossible: if that response stream is gone, its call is cancelled rather than exposed through replay.
193
+ 13. Daemon delivery is at-least-once until `tool_result_ack`. The generation guard, idempotent already-terminal handling, and authoritative `resume_calls` set make duplicate delivery converge without reviving removed calls. Modern response closure and legacy explicit cancellation remove their respective pending ownership before a late result can be delivered. On readiness handover, the runtime cancels active calls and queued results absent from `resume_calls` before accepting `ready_ack`.
194
+ 14. A tool deadline cancels only that operation and never infers daemon death from tool duration. The independent daemon-liveness alarm owns socket invalidation. If same-instance readiness does not return before the grace deadline, the Worker rejects the detached request and the local runtime cancels ordinary calls, terminates their process trees, and discards queued results. A newly started daemon has a different instance identifier and cannot inherit prior calls.
195
+ 15. `start_job` is different: after durable acceptance, the detached runner is no longer bound to an MCP response stream or daemon socket. Later cancellation uses `cancel_job` or the local CLI.
183
196
 
184
- Duplicate in-flight JSON-RPC IDs are rejected only within the same authenticated MCP session. The request key includes OAuth token identity, the HMAC-bound MCP session, JSON-RPC id type, and id value, so separate initialized clients may safely reuse the same numeric id.
197
+ Modern HTTP JSON-RPC IDs are scoped to each request or response stream and are not used as a token-wide duplicate key, so independent clients may reuse the same typed ID. Legacy HTTP duplicate detection and cancellation are scoped to OAuth token + signed MCP session + typed JSON-RPC ID. Stdio has one process-local in-flight ID index because all requests share one explicit transport channel.
185
198
 
186
199
  ## Stdio request lifecycle
187
200
 
188
201
  1. The local client launches `machine-mcp stdio` with a workspace and profile.
189
- 2. The server negotiates one of the supported MCP versions and appends bounded local `session_bootstrap` instructions when available.
202
+ 2. Modern MCP `2026-07-28` requests carry version and client capabilities in every request `_meta` and require no initialization handshake. A legacy MCP `2025-11-25` client opens with `initialize`; only that adapter returns initialization instructions. `session_bootstrap` remains an explicit tool in both eras.
190
203
  3. Tool discovery is generated from the same catalog and policy used by remote mode.
191
204
  4. Each call receives an internal random call ID used only for cancellation and process tracking.
192
205
  5. Input is parsed as incrementally bounded newline-delimited JSON-RPC, so an oversized line is discarded before unbounded buffering and the next line can still be processed. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
@@ -235,13 +248,13 @@ Large object results use `structuredContent` as the authoritative representation
235
248
 
236
249
  Startup-lock waits, daemon takeover, process-session reads, managed-job recovery handoff, browser/page waits, application-cache freshness, and in-memory duration metrics use monotonic elapsed time, so wall-clock correction cannot extend or prematurely terminate their configured duration. Persisted timestamps and retention/credential expiry continue to use wall time. Process sessions retain bounded byte buffers with monotonic offsets, accept bounded stdin, support short output/exit waits, and are capped per runtime. Valid UTF-8 is returned as text; byte slices that are not valid UTF-8 also include lossless base64 data. Head/tail previews trim incomplete UTF-8 boundary code points instead of introducing replacement characters. Session IDs are random. Running sessions are killed on runtime stop or non-recoverable daemon replacement; a transient same-process relay disconnect enters bounded recovery instead of being treated as cancellation.
237
250
 
238
- Child processes run in a separate process group where supported. Timeout, explicit cancellation, reconnect-grace expiry, runtime shutdown, and non-recoverable replacement send termination to process trees, with a referenced forced-escalation timer that remains alive even when the direct child exits before a resistant descendant. POSIX ownership is captured before `SIGTERM`, refreshed immediately afterward to include descendants created near the timeout boundary, and revalidated before `SIGKILL` by PID, process start time, and process-group ID. If a full process-table snapshot is unavailable, each captured PID is queried directly; ambiguous identity or PID reuse still fails closed. Windows uses tree-aware task termination.
251
+ Child processes run in a separate process group where supported. Timeout, explicit cancellation, reconnect-grace expiry, runtime shutdown, and non-recoverable replacement send termination to process trees, with a referenced forced-escalation timer that remains alive even when the direct child exits before a resistant descendant. The exported defaults are a two-second graceful interval and a three-second monotonic ownership-verification budget; lifecycle tests derive their observation deadline from those constants rather than duplicating an exact wall-clock boundary. POSIX ownership is captured before `SIGTERM`, refreshed immediately afterward to include descendants created near the timeout boundary, and revalidated before `SIGKILL` by PID, process start time, and process-group ID. If a full process-table snapshot is unavailable, each captured PID is queried directly; the complete full-table plus targeted fallback decision shares that one three-second monotonic budget, so descendant count cannot multiply its requested worst-case latency. Every synchronous `ps` deadline uses `SIGKILL`, because the Node default `SIGTERM` timeout can remain blocked when the helper does not terminate. An empty ownership snapshot, ambiguous identity, changed start timestamp, or PID reuse fails closed before forced escalation. Child execution prefers the ordinary `close` event so stdout and stderr drain completely; after an observed `exit`, a one-second fallback destroys only residual stdio handles and settles exactly once if libuv never emits `close`. Windows uses tree-aware task termination.
239
252
 
240
253
  Managed jobs use the same argv/environment primitives but a different lifecycle. Each job is capped at 16 main and 16 finally steps, 50 retained jobs, 64 registered resources, 8 MiB of referenced resource bytes, 512 KiB of temporary-file content, and bounded per-step output. They are non-interactive. Resource paths/stdin/environment are injected only inside the runner. Exact resource output redaction is defense in depth; discard capture is the strong option when a command may echo credentials.
241
254
 
242
255
  ## Worker deployment convergence
243
256
 
244
- Worker deployment is an explicit two-evidence state machine owned by `worker-deployment.mjs`. Wrangler upload is the authoritative remote write. Public `/healthz` is a subsequent read used to verify identity and package version; it is not a transaction commit signal for the upload. After a successful Wrangler result, local state atomically records the detected `workers.dev` URL, MCP URL, content/secret fingerprint, deployed package version, and timestamp before health verification begins. If verification is ambiguous, the next start compares the same fingerprint and performs a read-only verification rather than repeating the remote write.
257
+ Worker deployment is an explicit evidence state machine owned by `worker-deployment.mjs`. Wrangler upload is the authoritative remote write. Public `/healthz` is a subsequent read used to verify Worker identity and package version; it is not a transaction commit signal for the upload and does not attest the active device-authentication secret. After a successful Wrangler result, local state atomically records the detected `workers.dev` URL, MCP URL, content/secret fingerprint, deployed package version, and timestamp before health verification begins. If verification is ambiguous, the next start compares the same fingerprint and performs a read-only verification rather than repeating the remote write. Owner-authorized candidate activation adds the missing end-to-end evidence: device preflight, signed challenge authentication, and readiness probing. An explicit authentication rejection after current-version health permits one same-name redeployment with the unchanged selected identity; ordinary timeout, proxy, TLS, network, and temporary health failures do not.
245
258
 
246
259
  `worker-health.mjs` owns bounded health I/O: exact HTTPS `workers.dev` origin and Worker-name validation, environment-proxy selection, request timeout, redirect rejection, response-size limit, JSON/identity/version validation, and coarse error classification. `network-proxy.mjs` is shared by remote HTTP health probes and WebSocket relay construction so both paths honor `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` without exposing proxy details. Local browser-broker health uses `loopback-health.mjs`, which accepts only canonical `http://127.0.0.1:<port>/healthz`, disables agent reuse, bounds the response, and deliberately bypasses environment proxies. Definitive stale evidence is retried for propagation and then permits a same-name redeploy; timeout, TLS, network, proxy, and temporary server failure remain ambiguous and fail without upload.
247
260
 
@@ -277,7 +290,7 @@ Browser-origin handling separates CORS response sharing from protocol authentica
277
290
 
278
291
  ## Observability
279
292
 
280
- Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, managed-job counts, resource alias names without paths or values, relay route state without endpoint details, authenticated/probing/ready socket counts, end-to-end readiness evidence, local execution guardrails, explicit OS-enforcement gaps, and privacy-preserving capability-routing evidence. It separates the daemon capability ceiling from the authenticated account authority: `daemon.policy`/`daemon.tools` retain the pre-role ceiling, while `authorization.effective_policy`/`authorization.effective_tools` and the top-level `tools` report the role-intersected authority before any host-side filtering. It explicitly reports that the host-exposed subset is unknown to the server. `diagnose_runtime` runs fixed local probes and explicitly reports that its own request reached the daemon.
293
+ Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, managed-job counts, resource alias names without paths or values, relay route state without endpoint details, authenticated/probing/ready socket counts, end-to-end readiness evidence, local execution guardrails, explicit OS-enforcement gaps, and privacy-preserving capability-routing evidence. It separates the daemon capability ceiling from the authenticated account authority: `daemon.policy`/`daemon.tools` retain the pre-role ceiling, while `authorization.effective_policy`/`authorization.effective_tools` and the top-level `tools` report the role-intersected authority before any host-side filtering. It explicitly reports that the host-exposed subset is unknown to the server. The Worker projects a remote-only foreground timeout schema of 1–85 seconds from the broader local catalog and rejects larger values before any daemon message is sent. `diagnose_runtime` runs fixed local probes, explicitly reports that its own request reached the daemon, and on macOS projects the default route into a coarse VPN/TUN interception class without returning interface or endpoint data.
281
294
 
282
295
  Foreground logging defaults to `info`; autostart uses `warn`. Authenticated readiness, persistent degradation, and recovery are user-visible state transitions. Brief relay interruptions, raw transport close details, retry timing, and all per-tool starts/successes/failures/cancellations/durations are debug-only. Unexpected local and Worker infrastructure errors are reduced to classes. Messages, strings, arrays, object depth/key counts, and serialized fields are bounded.
283
296
 
@@ -285,9 +298,9 @@ Cloudflare sampling is size control rather than an audit log. The project intent
285
298
 
286
299
  ## Release integrity
287
300
 
288
- Repository-local checks cannot prove the ordinary deployed path. `local-release-acceptance.mjs` builds the exact tarball and promotion-content digest. The owner executes `release:candidate:activate`, which installs the tarball under the private state root and invokes the extracted `runtime-activation` state machine. That state machine stops only a verified service owner, authenticates the candidate daemon through the real Worker, proves relay readiness, installs the service definition, releases startup/daemon ownership in a defined order, starts the login service, and verifies the exact Worker and daemon versions. Fault-injection tests cover foreground-owner conflict, installation failure cleanup, failed service start, and convergence timeout.
301
+ Repository-local checks cannot prove the ordinary deployed path. `local-release-acceptance.mjs` builds the exact tarball and promotion-content digest. The owner executes `release:candidate:activate`, which installs the tarball under the private state root and invokes the extracted `runtime-activation` state machine. The transaction acquires the machine-service lock before the workspace startup lock, rejects foreground or unverifiable ownership before provider mutation, authenticates the candidate daemon through the real Worker, and proves relay readiness before writing the service definition. Installation commits a machine-global owner record for the exact workspace, state root, entrypoint, and version. The login-service handoff succeeds only when that owner's daemon lock publishes the post-`ready_ack` readiness checkpoint; provider-active state alone cannot satisfy acceptance. A first explicit device-authentication rejection triggers one same-name, same-identity repair deployment and bounded candidate retry. If remote preparation has already advanced the deployment and activation still fails, cleanup installs and starts the compatible candidate service rather than restoring an incompatible previous runtime. Before remote transition, an older service is considered restored only when the same version and entrypoint reappear as a verified service daemon. The activation wrapper has no outer transaction-wide `SIGKILL`; each deployment, network, relay, service-manager, and convergence stage owns its bounded deadline so cleanup cannot be bypassed. Fault-injection tests cover lock ordering/release, pre-mutation foreground refusal, owner transaction failure, missing/corrupt/pending owner state, readiness failure, authentication repair and exhaustion, compatible-service recovery, legacy identity restoration, cleanup aggregation, failed service start, and convergence timeout.
289
302
 
290
- Accepted prereleases use explicit npm/GitHub channels and a registry-verified activation record. `release-soak.mjs` enforces elapsed major/minor/patch observation windows. `promotion-digest.mjs` hashes the npm package inventory, file modes, and bytes while normalizing only synchronized version metadata; stable release is blocked if any functional packaged content differs. Guarded push, portable CI acceptance, GitHub source release, npm publication, and stable publication all validate the relevant acceptance/soak evidence. Release commands require `HEAD === origin/main` and never push `main` implicitly.
303
+ Accepted prereleases use explicit npm/GitHub channels and a registry-verified activation record. `release-soak.mjs` enforces elapsed major/minor/patch observation windows. `promotion-digest.mjs` hashes the npm package inventory, file modes, and bytes while normalizing only synchronized version metadata; stable release is blocked if any functional packaged content differs. Guarded push, portable CI acceptance, GitHub source release, npm publication, and stable publication all validate the relevant acceptance/soak evidence. GitHub tag/Release mutation additionally requires an explicit confirmation flag and real owner TTYs before any fetch or verification, then holds a process-identity publication lock at the common Git state path so linked worktrees share the same owner. Release commands require `HEAD === origin/main` and never push `main` implicitly.
291
304
 
292
305
  Cross-platform evidence remains independent. `scripts/github-release.mjs` queries CI, CodeQL, Governance, and Scorecard for the exact `origin/main` commit and requires the newest push-triggered run for each workflow to be completed with `success` before it creates or verifies a version tag, GitHub Release, or package asset. Pull-request runs, older successful runs, pending runs, and successful runs for another SHA do not satisfy the gate. The workflow selection policy is isolated in `scripts/release-ci.mjs` and tested independently.
293
306
 
package/docs/AUDIT.md CHANGED
@@ -1,5 +1,87 @@
1
1
  # Security and privacy audit notes
2
2
 
3
+ ## 2026-07-29 version 3.0.0-beta.26 GitHub publication ownership audit
4
+
5
+ After beta.25 was accepted and merged, several non-interactive paths attempted to start its guarded GitHub prerelease command: foreground MCP execution, detached shell/session variants, launchd handoff, and a managed job. Host lifecycle ownership terminated the long-running descendants before publication, and the managed job preserved a terminal `SIGTERM` result. A later calendar-triggered launchd attempt reached the concurrently prepared beta.26 guard and was rejected before fetch. No beta.25 tag, GitHub Release, or npm version was created. The event showed that candidate acceptance and green exact-commit CI are durable evidence but not proof of fresh operator intent for a later remote publication.
6
+
7
+ Beta.26 makes publication a distinct owner-terminal workflow boundary. `--publish`, `--publish-prerelease`, and `--backfill` require TTY-backed stdin/stdout/stderr plus `--owner-terminal-confirm` before fetch, full checks, tag creation, or any remote call. A process-identity lock in an owner-only common Git state directory serializes publication across the main checkout and linked worktrees, and safely reclaims only a verified stale owner. Release failures now throw through the lock scope instead of exiting before cleanup. Ordinary checks, candidates, acceptance, branch/PR work, Bash execution, and npm publication policy remain separate. This does not claim cryptographic human presence: arbitrary same-user code can allocate a pseudo-terminal, so stronger adversarial separation still requires an external protected release environment or OS user-presence mechanism.
8
+
9
+ Candidate verification under concurrent simulator and Rust/Wasmtime workloads exposed test-infrastructure false negatives rather than product failures: the publication common-Git-directory probe had a 5-second local metadata deadline, runtime Git success fixtures and the repository-root subprobe used hidden 10-second deadlines beneath 30-60 second Git operations, local self-test success fixtures used 5-30 second process/CLI budgets, the managed-job timeout-tree test read its descendant PID without first observing the fixture checkpoint, and `diagnose_runtime` reused a 5-second process-completion budget that was too short to distinguish scheduler starvation from a broken spawn boundary. Read-only Git metadata probes remain fail-closed with bounded 30-second deadlines; non-timing process and self-test fixtures use named 30-60 second budgets and phase-labelled errors; managed-job and shell process-tree tests wait for descendant-PID publication before timeout/cleanup assertions; maintenance-lock contention uses an explicit parent/child release handshake instead of a wall-clock holder; runtime diagnostics use a separate 30-second direct-process and shell health budget; process-lock and daemon-takeover helper processes explicitly receive `NODE_V8_COVERAGE=""` because Node otherwise reinjects the parent coverage directory into every child, multiplying profiler startup without adding threshold-relevant evidence; the exclusive-create proof uses four simultaneous child processes, preserving the cross-process single-winner invariant without multiplying Node cold-start cost across twelve contenders; daemon readiness and successful takeover/stop paths use a named 30-second observation budget; managed-job runners remain covered, while their trivial marker-writing child steps receive `NODE_V8_COVERAGE=""` and a 120-second success budget. Approval, resource-validation/redaction, bounded-output, discard-output, and cleanup/recovery fixtures use the same named success budget; the managed-job tree fixture uses a 180-second timeout plus a 150-second descendant-readiness window so timeout/tree-kill is evaluated only after the resistant descendant exists; cancellation semantics remain separate. The aggregate-output proof uses four steps with a 600-second observer, and the ordinary terminal observer is 480 seconds, exceeding a three-phase 3×120-second plan plus startup margin without altering production timeout semantics. Managed-job CLI list/inspect/submit/read fixtures use a distinct 120-second observation budget and preserve status/signal/error diagnostics because they validate state projection rather than a latency SLA. These nested CLI probes explicitly receive `NODE_V8_COVERAGE=""`; top-level local-self coverage remains active, and dedicated CLI-entrypoint plus managed-job fixtures retain the relevant gated evidence. The same stress run exposed a product race in the ten-second recovery grace period: a spawned runner could still be initializing before it created `runner.pid`, allowing a concurrent read to relaunch the job as interrupted. Launch now uses an owner-only provisional PID/one-time-token claim published by the parent; the child verifies and atomically upgrades it before executing, and claim collisions terminate the spawned child fail-closed. Under severe scheduler starvation, macOS could leave an already-exited child as a zombie while libuv had not yet delivered `exit` or `close`; the timeout callback now recognizes that exited-but-undrained state and the shared settlement fallback re-reads the real exit code instead of reporting a false timeout. Browser-broker connection, rejection, close, handshake, and state-convergence fixtures use a separate bounded 30-second observation window; the one-second request timeout and normalized browser-operation deadlines remain unchanged. Explicit short refusal, force-escalation, timeout, cancellation, and process-tree semantics remain covered independently.
10
+
11
+ ## 2026-07-29 version 3.0.0-beta.25 MCP 2026-07-28 migration and repository audit
12
+
13
+ The audit treated MCP `2026-07-28` as a protocol-state-machine replacement rather than a metadata bump. The project now has a modern stateless core and an explicit MCP `2025-11-25` compatibility adapter. Modern HTTP/stdio requests carry version and capabilities per request, use `server/discover`, receive no MCP session, never enter the legacy `Last-Event-ID` recovery store, and scope cancellation to the request or response stream. Legacy initialization, signed session, duplicate/cancellation domain, and bounded recovery remain isolated behind the adapter.
14
+
15
+ The review found and corrected several non-obvious protocol defects: header/body version mismatch had to take precedence over unsupported-version classification; a modern `initialize` request was incorrectly entering the legacy adapter; OAuth token plus JSON-RPC ID was incorrectly acting as a global modern request identity; unknown tools and malformed arguments were being wrapped as tool failures instead of protocol `-32602`; `subscriptions/listen` accepted missing or mistyped filters; non-object `structuredContent` values were discarded; and the first modern stream design retained an unresolved terminal Promise across Durable Object events. The final design uses one direct stream request plus a stream-scoped cancellation control that indexes only the active pending call; there is no modern prepare/subscribe descriptor, terminal-result registry, or replay state. Modern same-token/same-ID concurrency, removed methods, malformed non-dispatch, filter validation, arbitrary JSON structured content, and deterministic cancellation-control delivery now have direct regression coverage. Wrangler does not reliably surface raw TCP disconnects to local Worker stream callbacks, so deployed-edge cancellation remains a candidate-activation observation rather than a falsely claimed emulator proof.
16
+
17
+ A shared bounded JSON Schema 2020-12 validator now enforces the catalog before Worker dispatch and again at the local runtime boundary. Unsupported dialects or keywords fail during catalog compilation, automatic network `$ref` dereference is impossible, and schema depth/node/pattern/issue plus runtime-step budgets are fixed. The object path no longer calls `Object.keys()` before budgeting: each own property consumes work, so an 8 MiB high-cardinality object cannot bypass the traversal ceiling. Open modern `_meta`, capability-extension, and subscription-filter JSON is independently capped at 4,096 structural nodes, 32 levels, and bounded key lengths; resource subscription lists accept at most 256 bounded strings. One role-aware inspector is shared by modern and legacy Worker dispatch; it rejects hidden tools, missing names, non-object arguments, and schema failures before daemon send or legacy resumption allocation with bounded `-32602` data and `side_effects_started=false`.
18
+
19
+ The cancellation review found a DPoP-specific failure that ordinary Bearer tests would miss. The private `modern-cancel` control originally copied the public Authorization and DPoP headers and re-entered access-token verification after the original proof JTI had been consumed. The final design treats the random 256-bit stream ID as an internal-only cancellation capability: caller-supplied control headers are stripped by the outer Worker, cancellation is handled before OAuth inside the Durable Object, and the control request carries no OAuth/DPoP credential. Actual `/mcp` requests now also enforce the Streamable HTTP Origin rule; unrelated or opaque origins fail with 403, while OAuth navigation routes retain their separate behavior. CORS no longer reflects arbitrary `Mcp-Param-*` names and bounds the complete preflight header list.
20
+
21
+ The official MCP conformance checkout was run against the real local Wrangler/OAuth/daemon integration through a test-only bearer-injecting loopback proxy. A fresh checkout at `49103de6ed70804e940637bf3e9e29e4a3f54e64` (`0.2.0-alpha.10`) reported fourteen upstream dependency advisories, including seven high-severity findings; those packages remain outside the project dependency graph and candidate. The runner receives only a loopback proxy URL, never the injected bearer, is used as a short-lived audit tool, and is deleted after execution. The driver now rejects missing, symlinked, unlocked, malformed, or dependency-uninstalled checkouts before starting Worker integration, replacing ambiguous `spawn ENOENT` and `tsx not found` failures with explicit diagnostics. `http-header-validation` passes without exclusions. `server-stateless` and `caching` pass with check-scoped expected failures only for conformance-only diagnostic tools and prompt/resource feature families the production server does not advertise. The proxy is loopback-only, accepts only relative `/mcp`, maps to the exact validated HTTPS or loopback upstream path, refuses alternate same-origin paths and absolute-form targets before injecting the bearer, bounds request bodies, settles aborted uploads, and terminates the runner's process tree on timeout. The baseline cannot hide an unrelated regression and becomes stale-failing when an excluded check starts passing. The alpha conformance package is intentionally not a project dependency.
22
+
23
+ The dependency pass found Wrangler `4.115.0` as the only newer direct pin. Its bundled workerd remains the already reviewed `1.20260722.1`, while Miniflare advances to `4.20260722.1`; the exact workerd lifecycle-script approval therefore remains unchanged. The relevant Wrangler behavior is bounded `429` handling: reasonable `Retry-After` values are honored, waits above sixty seconds fail fast, and surfaced command failures include `retry_after_ms`. This improves candidate deployment/upload diagnosis without converting a cloud rate limit into an unbounded activation transaction. Complete and production dependency audits remain at zero known vulnerabilities, and registry signature verification reports no missing or invalid signatures.
24
+
25
+ The first authoritative full run exposed two verification defects rather than production failures. Under V8 coverage load, the shell process-tree fixture could reach its 200 ms timeout before the helper process had created `child.pid`; the test then misclassified absence of the fixture as a cleanup failure. The fixture now starts the actual bounded operation, waits up to four seconds for a syntactically valid descendant PID, and still relies on the five-second production timeout plus the normal process-tree termination path—there is no blind sleep or leaked process on failure. The next coverage pass correctly identified that the newly exported Worker argument validator lacked a direct call in coverage fixtures. Positive, additional-property, and unknown-tool paths were added, restoring `src/worker/tool-catalog.ts` to 100% function coverage without weakening its 95% gate.
26
+
27
+ A later candidate run exposed a second exact-boundary test race. The resistant descendant check waited exactly five seconds after the foreground timeout, equal to the production two-second graceful interval plus the maximum three-second process-ownership verification budget. A slow final `ps` identity probe could therefore complete just after the assertion. The test now imports those production constants and adds a fixed two-second scheduling margin; three consecutive self-test runs pass, and the runtime escalation algorithm and fail-closed ownership checks are unchanged.
28
+
29
+ A subsequent candidate gate exposed a managed-job verification race rather than a production cleanup failure. The runner deliberately persists a terminal status with `artifact_cleanup_pending=true` before deleting private runtime resource copies, temporary files, the active plan, PID claim, and cancellation artifacts, then confirms the status with pending cleared. The integration helper returned on the first terminal status and immediately asserted that those artifacts were absent, so scheduler load could expose the valid intermediate checkpoint. Diagnostic repetitions observed pending terminal states directly. The helper now waits for both a terminal status and cleanup confirmation, with deterministic checkpoint assertions and ten consecutive managed-job integration passes. The production persistence ordering and recovery semantics are unchanged.
30
+
31
+ A subsequent design review used Miguel Salinas's camelAI Durable Object/Code Mode write-up as a prompt for broader research rather than as an implementation template. Cloudflare's Code Mode and Dynamic Workers material demonstrates that compact code plans and lightweight isolates can reduce tool-context cost, but the feature remains experimental and solves a different trust/deployment problem. Machine Bridge therefore does not replace or demote local Bash, add model-generated JavaScript execution, or add a new sandbox dependency in beta.25. Its owner-facing product value depends on efficient direct shell access.
32
+
33
+ The applicable finding was tool-harness scaling. Recent routing studies report material accuracy degradation as catalogs grow and gains from task-specific shortlisting; production work on skill descriptions identifies overlapping descriptions as a distinct collision source; ToolBench-X shows that explicit recovery guidance often matters more than additional inference under tool failures; and ToolPrivacyBench shows that successful completion does not imply need-to-know information flow. The project now returns bounded set-level execution routes, ambiguity and fallbacks; rewrites high-collision descriptions; filters discovery by effective account authority; and exposes failure-aware advice without enforcing a smaller tool surface. Direct Bash remains the general escape hatch. The new bilingual regression corpus fixes expected route behavior for commands, shell, process sessions, jobs, Git/files, browser, applications, diagnostics, and protected resources.
34
+
35
+ The same review found that capability resolution used the daemon's full policy when composing application/browser metadata even after the relay had established a narrower account authority. That created an inventory side channel for delegated roles. `LocalRuntime` now passes the request's effective policy to bootstrap/resolution, and application scanning is skipped before touching the OS when the role cannot invoke it. Matching capability fingerprints may omit repeated static instructions, but every task still rescans and reranks dynamic capabilities; the fingerprint is neither authorization nor conversation identity. Because omission turns the fingerprint into a correctness boundary, it now binds target/scope, instruction provenance/precedence, skill sources, and complete command definitions rather than only content hashes and argv. The route engine reads only frozen policy-visible name/title/description metadata rather than cloning complete input schemas on every call, and its envelope identifies its schema plus non-probabilistic score semantics.
36
+
37
+ A second supply-chain pass found that an ambiguous unscoped `cyclonedx-npm` command can resolve to a dependency-confusion placeholder that prints a warning yet exits successfully, producing no SBOM. The project does not depend on that package: `sbom:test` now invokes the pinned npm CLI directly, bounds and parses its CycloneDX 1.5 JSON, verifies the current package identity and root dependency graph, rejects local filesystem paths, and is part of the full candidate gate. The CI package-audit job retains its independent temporary-file validation.
38
+
39
+ Release-state review found that a pending tarball could remain internally consistent with its manifest after later source edits. Candidate start/activation now recomputes the current promotion-content digest and rejects a mismatch before tarball verification, npm installation, Worker deployment, or service mutation; the stale local candidate was removed. Critical coverage now directly gates the shared protocol/subscription/schema modules, role-aware input inspection, modern controller/proxy/stream, HTTP contract, and candidate guard. The immutable beta.24 identity was not reused; the unreleased work is beta.25. No npm publication, tag, GitHub release, Worker deployment, service replacement, acceptance, commit, or push is part of this audit.
40
+
41
+ ## 2026-07-28 version 3.0.0-beta.24 candidate-activation convergence audit
42
+
43
+ Owner-machine activation of the exact beta.23 candidate exposed a release-state defect that repository-local health checks could not prove. The same-name Worker reported the expected package version and normal health, but the candidate daemon received an HTTP authentication rejection before the Worker registered a WebSocket candidate. A forced redeployment using the unchanged local device identity then completed preflight, challenge authentication, readiness probing, and ordinary MCP delivery without rotating credentials. The supported conclusion is that version health and the locally recorded deployment fingerprint did not prove that the active edge deployment used the intended device-authentication material.
44
+
45
+ The failure also exposed unsafe compensation ordering. Activation had already advanced the Worker, then restarted the previous service definition after candidate authentication failed. Because Worker and daemon versions are an exact current-only contract, the restored older daemon could not reconnect and the system was left without a remote control path. A Windows task that exits with code zero but does not remain active is now a failed persistent start, not successful service evidence. Systemd unknown or maintenance states fail before mutation, while activating/reloading states retain restoration intent. Beta.24 separates recovery by commit boundary: before remote preparation, a provider carrying verified restoration intent may be restored; after remote preparation, cleanup installs and starts the compatible candidate service instead of reviving a known-incompatible daemon.
46
+
47
+ Candidate activation now treats explicit device-authentication rejection as positive convergence evidence distinct from ambiguous timeout, TLS, proxy, or health failure. It performs at most one same-name, same-identity forced deployment, never requests secret rotation, and bounds candidate startup to three attempts. Persistent failure is still returned to the operator. If remote preparation occurred, the error also states whether a compatible candidate service was started for automatic recovery; installation, start, lock-release, and runtime-stop failures remain aggregated.
48
+
49
+ Fault-injection tests reproduce the initial rejection, one repair deployment, successful second authentication, repeated rejection exhaustion, compatible-service forward recovery, service-stop refusal, ambiguous provider state, daemon-owner ambiguity, malformed version/wait/repair inputs, invalid attempt bounds, missing lock-release contracts, and cleanup failure. The activation module is now part of the critical coverage gate at 100% function and 80% branch coverage. No account identifiers, endpoint names, device keys, filesystem paths, command output, or credentials are added to tracked audit evidence or operational logs.
50
+
51
+ Remote foreground timeout validation was also tightened at the Worker execution boundary. When `timeout_seconds` is present it must be a numeric safe integer from 1 through 85; strings, fractions, zero, negative values, non-finite values, and larger values are rejected before a daemon `tool_call` with `side_effects_started=false`. This prevents direct JSON-RPC callers from bypassing the generated schema through permissive coercion.
52
+
53
+ The process-tree review found that an empty POSIX ownership snapshot still authorized forced escalation while the ChildProcess object had not reported exit. That was inconsistent with the documented fail-closed identity rule and could, under an extreme exit-event/PID-reuse race, signal an unrelated reused process group. Beta.24 now requires a non-empty captured ownership set and exact PID/start-time continuity before `SIGKILL`; failed inspection may leave a resistant descendant for operator cleanup. The child-settlement, process-ownership, and system-route helpers are included in strict checked-JavaScript contracts, and the child-settlement state machine has an independent 100% function/89% branch coverage gate.
54
+
55
+ A second full-plan reproduction showed that the first mitigation was incomplete: the detached runner could still remain in `running` beyond twenty seconds with no output. An isolated Node experiment reproduced the causal mechanism. `spawnSync` timeout defaults to `SIGTERM`; when the synchronous child ignores or fails to complete after that signal, Node continues waiting and the runner event loop cannot execute its own settlement timers. The same child returned at the declared boundary when the timeout used `SIGKILL`. Beta.24 therefore hard-bounds synchronous process-identity, process-tree, delegated-sandbox, macOS trust-broker, candidate-activation, prerelease-install, and synchronous verification helpers with `killSignal: "SIGKILL"`. A trust-broker child killed because its synchronous deadline expired is classified as `ETIMEDOUT` before signal-based code-signing diagnostics, so timeout is not misreported as an entitlement failure. Architecture checks reject any listed runtime probe whose timeout count is not matched by a hard-kill signal, and behavior tests execute an uncooperative child that ignores `SIGTERM`.
56
+
57
+ Candidate preparation also exposed a managed-job timeout race under repeated full-plan execution. A timed-out step and its descendants had exited, but macOS/libuv occasionally omitted the ChildProcess `close` event after `exit`; the runner therefore retained an unresolved step promise. Separately, process-tree ownership fallback could spend a full three seconds on the process table and another three seconds per captured member. Beta.24 gives each ownership decision one shared three-second monotonic budget and adds a one-second post-`exit` close fallback that preserves normal output-drain priority, destroys only residual handles, and settles through the existing terminal persistence path. Tests cover direct close, exit fallback, close/fallback race suppression, global timeout allocation, descendant death, terminal status, and detached runner exit.
58
+
59
+ Beta.23 is blocked and was not accepted, published, or promoted. Beta.24 requires a new exact candidate, owner activation, observed live Worker/service verification, and acceptance before any Git push or prerelease release.
60
+
61
+ ## 2026-07-28 version 3.0.0-beta.23 workflow-closeout interruption and repository audit
62
+
63
+ The current incident was not a local daemon crash. During the reproduced failure, launchd retained the same daemon PID, process start time, and `runs=1`; the machine was awake with active user and Xcode assertions. The remote surface temporarily reported no connected daemon and recovered roughly one minute later. The operating-system default route was carried by Karing's TUN interface, normal Worker health succeeded through that route, and an interface-bound physical-network probe did not. The supported conclusion is therefore a transient relay path failure inside or beyond the system VPN/TUN boundary. Machine Bridge can detect and recover its socket, but cannot select or repair a third-party VPN node.
64
+
65
+ Beta.22 still contained an independent amplification defect. The shared local tool schema accepted foreground timeouts up to 600 seconds and commonly defaulted to 120, while the hosted Worker silently capped configurable foreground execution at 85 seconds plus five seconds for terminal delivery. That mismatch could let local mutation work finish before the Worker cancelled the call, producing exactly the misleading state “transactional patch applied, validation interrupted.” Beta.23 makes the remote contract truthful: Worker `tools/list` advertises an 85-second maximum while preserving 30- or 60-second tool defaults, and any larger request fails before daemon dispatch with a structured no-side-effect marker. Local/stdio operation keeps its broader schema because it does not traverse the hosted request boundary.
66
+
67
+ The audit added a privacy-bounded system-route diagnostic rather than logging raw networking state. On macOS it invokes only the fixed `/sbin/route -n get default` probe and projects the interface into a coarse route class and interception boolean. Interface names, IP addresses, DNS answers, Worker endpoints, proxy URLs, and credentials are omitted. Unsupported platforms and probe failures degrade to skipped diagnostic checks with coarse error classes.
68
+
69
+ The second repository review found one stale closeout sentence in `TESTING.md`; it still required beta.21 gates despite two later candidates. That version-specific assertion is replaced by a current-candidate rule. No new secret-bearing default logs, raw command/result logging, public administration route, unbounded request body, hidden compatibility execution path, or dependency-range relaxation was accepted. The first route-diagnostic draft exceeded the existing `runtime-diagnostics.mjs` line budget; the implementation was extracted into a dedicated boundary instead of weakening the architecture gate.
70
+
71
+ This source change does not prove third-party TUN reliability and does not deploy a Worker, activate the candidate, replace the running beta.22 daemon, rotate credentials, publish npm, push Git history, create a tag, or record live acceptance. Those remain explicit owner operations after complete repository verification.
72
+
73
+ ## 2026-07-28 version 3.0.0-beta.22 ChatGPT interruption and full repository audit
74
+
75
+ The visible symptom combined three different mechanisms that must not be collapsed into one diagnosis. First, historical 11–34 minute relay gaps aligned with macOS clamshell sleep and DarkWake intervals; those records are expected machine suspension, not evidence of an active-use Cloudflare or daemon failure. Second, a controlled foreground command requested 120 seconds but the hosted tool path abandoned it at approximately 100 seconds. Third, the live Worker retained durable `exec_command` records after the local runtime had already completed and removed the corresponding operation. The latter two mechanisms explain active-use ChatGPT interruption and ghost pending-call growth.
76
+
77
+ The terminal-delivery protocol had a concrete loss window. The local runtime deleted a completed result as soon as `WebSocket.send()` accepted the frame, although that call proves only local queueing. If the connection ended before the Worker committed its generation-guarded terminal storage transaction, the runtime had neither an active operation nor a retained result to replay, while the Worker still owned the durable call. Beta.22 adds `tool_result_ack`: the runtime retains and periodically replays terminal results until acknowledgement; the Worker acknowledges only a transient settlement, a committed durable settlement, or an idempotent already-terminal duplicate. A result from a stale WebSocket generation is never acknowledged. Durable persistence failure now propagates instead of logging and falsely incrementing completion.
78
+
79
+ The timeout path also crossed responsibility boundaries. A single call timeout used a 45-second socket-silence heuristic to invalidate the complete daemon connection even though daemon liveness already has an independent 90-second alarm and readiness state machine. A slow command could therefore interrupt unrelated reads and make ChatGPT lose every local surface. Beta.22 removes socket invalidation from both transient and durable call expiry; timeout sends best-effort per-call cancellation only. The liveness alarm remains the sole owner of connection invalidation. Because the hosted foreground tool path was observed to stop near 100 seconds, configurable remote operations are capped at 85 seconds plus five seconds for terminal delivery. Longer work belongs in `start_process`/`read_process` or managed jobs, subject to what the MCP host exposes.
80
+
81
+ The broader review found a separate operations defect not named in the report: active daemon logs were safely trimmed before startup but could grow without another bound during a very long service lifetime. Beta.22 reuses the existing secure in-place trim every 15 minutes. The maintenance path preserves regular-file, no-follow, single-link, owner-only mode, schema, UTF-8, and line-boundary checks; failures log only a coarse class. Static review found no new secret-bearing operational logs, raw command/result logging, unbounded Worker body, public control-plane path, or obsolete transient settlement branch. Intentional migration fixtures remain confined to tests and explicit state upgrade code.
82
+
83
+ Fault-directed tests cover connected and reconnect result retention, lost acknowledgement heartbeat replay, Worker acknowledgement identity, persistent terminal-write failure, stale-generation non-acknowledgement, host-safe timeout bounds, log-maintenance scheduling/failure containment, and the previous cancellation/reconnect matrix. The repository architecture line budget rejected an oversized first draft of the recovery module; the implementation was reduced below the existing boundary rather than weakening the gate.
84
+
3
85
  ## 2026-07-27 version 3.0.0-beta.21 relay-continuity and repository audit
4
86
 
5
87
  The reported symptom was a temporary loss of every local command surface, including `pwd`, followed by automatic daemon reconnection. Process evidence rejects the premise that the local daemon crashed: launchd retained one daemon PID, one start time, and `runs=1` throughout the observed interval. The failing layer was the relay/Worker connection and result-delivery path. Historical service logs also contain abnormal WebSocket closure and heartbeat-outage intervals that later recovered without a daemon restart. The exact network origin of each `1006` remains unknowable from those logs alone; a system VPN/TUN, proxy route, intermediary, or edge connection can all produce the same transport-level symptom.
@@ -12,7 +94,9 @@ Second, streamed calls returned from their initiating Durable Object event but r
12
94
 
13
95
  The broader repository review refused line-budget exceptions and extracted outer Worker routing, pending-call persistence, record validation, stream-index maintenance, durable settlement, result projection, alarm storage, and socket-attachment decoding into focused modules. It removed the obsolete transient `registerEvent` settlement branch, added architecture rules forbidding its return, preserved Node strip-only TypeScript compatibility, and added fault-directed tests for restart recovery, corrupt records, fixed storage-write budgets, alarm expiry, stale generations, exactly-once settlement, stable discovery, and disconnected execution. Logging and privacy rules prohibit arguments, results, request keys, account identifiers, raw call/connection IDs, private paths, and subscriber payloads from operational diagnostics.
14
96
 
15
- The workflow-level global verification deliberately repeated the complete project-native gate and exposed a separate real cleanup defect after two earlier green full runs. Under load, the POSIX process-tree ownership snapshot could miss a newly spawned descendant or the later full-table `ps` scan could fail; if the direct parent exited after `SIGTERM`, the conservative identity check then skipped `SIGKILL`, leaving an anti-`SIGTERM` descendant reparented to init. The fix refreshes process-group ownership after graceful termination and, during escalation, performs a targeted `ps -p` identity check for each captured PID when the full group scan yields no match. Five consecutive complete self-tests leave no descendant behind, while PID/start-time/PGID mismatch continues to suppress escalation.
97
+ The workflow-level global verification deliberately repeated the complete project-native gate and exposed a separate real cleanup defect after two earlier green full runs. Under load, the POSIX process-tree ownership snapshot could miss a newly spawned descendant or the later full-table `ps` scan could fail; if the direct parent exited after `SIGTERM`, the conservative identity check then skipped `SIGKILL`, leaving an anti-`SIGTERM` descendant reparented to init. The first fix refreshed process-group ownership after graceful termination and performed targeted `ps -p` identity checks for captured PIDs. A later full-plan coverage run still reproduced the boundary on macOS because the initial full-table snapshot itself could time out and produce no ownership to revalidate. Darwin now uses `ps -g <PGID>` for capture and group revalidation, avoiding the unrelated system-wide process table while retaining exact PID/start-time/PGID identity. The self-test wait includes margin beyond the two-second grace plus three-second inspection budget. Repeated complete self-tests and coverage leave no descendant behind, while empty, changed, or reused identity still suppresses escalation.
98
+
99
+ A final exact-command preflight exposed a separate namespace regression that unit and package tests had not exercised: the POSIX `defaultStateRoot()` fallback had accidentally been changed to the new machine-service control directory. The standard owner command therefore installed the candidate under `machine-bridge-mcp-control` and failed before foreground ownership inspection because that directory already contained control artifacts. The fallback is restored to `~/.local/state/machine-bridge-mcp`; the control directory remains the sibling `machine-bridge-mcp-control`. Injected POSIX, XDG, and Windows path tests require the two roots to differ, and the exact owner wrapper was executed against the live beta.23 foreground daemon to prove it now installs under the profile state root, rejects before Worker/service mutation, identifies the old runtime, and emits the verified recovery sequence.
16
100
 
17
101
  `npm run check:fast` passes all 63 repository tasks after the implementation. Full, dependency, package, Worker dry-run, privacy-history, and workflow-bundle verification remain separate evidence and must pass before merge readiness is claimed. This source audit does not deploy a Worker, replace the running daemon, activate a candidate, rotate credentials, publish npm, push Git history, create a tag, or record live acceptance.
18
102
 
package/docs/CLIENTS.md CHANGED
@@ -52,9 +52,13 @@ It is therefore an optional compatibility and reuse surface, not a replacement f
52
52
 
53
53
  The MCP specification defines stdio and Streamable HTTP as standard transports. In stdio, the host launches the server as a subprocess and exchanges newline-delimited JSON-RPC through stdin/stdout. Streamable HTTP runs the server independently behind an HTTP endpoint.
54
54
 
55
+ Machine Bridge supports two protocol eras on those transports. Modern clients use MCP `2026-07-28`: every request carries version and capabilities in `_meta`, `server/discover` replaces initialization, and an HTTP response stream belongs to exactly one request. Closing that stream cancels the request; it cannot be resumed with `Last-Event-ID`. Legacy MCP `2025-11-25` clients remain supported through a compatibility adapter that uses `initialize`, `Mcp-Session-Id`, and session-bound resumable delivery. A connection, process, or stdio lifetime is never treated as a conversation identity by the modern dispatcher.
56
+
55
57
  ## Automatic capability selection
56
58
 
57
- MCP initialization and `resolve_task_capabilities` give the host a current view of conservative built-in working agreements, bounded project facts, explicit global/project instructions, skills, explicit/automatic package commands, applications, and browser capability. The resolver rescans rather than relying on a stale dynamic tool list and can return the best matching skill instructions in one call. No instruction file is required for the default layers, and no repository file is written automatically.
59
+ For modern clients, `server/discover` supplies conservative server guidance, while `session_bootstrap` and `resolve_task_capabilities` explicitly refresh bounded project facts, global/project instructions, skills, explicit/automatic package commands, applications, browser capability, and task-specific execution routes. Legacy initialization returns the same bounded guidance through the compatibility adapter. The resolver scores route bundles instead of forcing a single tool: registered commands, Bash/direct argv, process sessions, managed jobs, files/Git, browser, applications, resources, and diagnostics may appear together with ambiguity and fallback metadata. This advice never removes a policy-visible tool; direct Bash remains available under shell-capable authority.
60
+
61
+ The resolver always rescans task-specific metadata. A client that already holds `refresh.fingerprint` may send it as `known_refresh_fingerprint`; if the static instruction/skill/command identity is unchanged, the response omits that repeated material but still returns fresh skill/command matches, application results, ranked tools, and route advice. Capability results are filtered by effective account authority, so a restricted role does not receive hidden application/browser/shell metadata from a fuller daemon. No instruction file is required for the default layers, and no repository file is written automatically.
58
62
 
59
63
  The host still owns the agent loop. A hosted client may use the recommendation automatically, ask for confirmation, expose only part of the catalog, or ignore server instructions. No MCP implementation can guarantee automatic invocation from the server side. Machine Bridge models that limitation explicitly instead of treating a recommendation as execution.
60
64
 
@@ -159,7 +163,7 @@ The local `full` profile controls Machine Bridge's own tool catalog, path resolv
159
163
 
160
164
  Machine Bridge itself does not block files because their names look sensitive. In remote mode, first inspect `server_info.authorization.effective_policy` and `effective_tools`; `daemon.policy` is only the local ceiling. If the effective profile is `full` and the effective tool is present but a direct call is still rejected before a structured result, the host/connector may have blocked delivery. If `diagnose_runtime` responds but its fixed process or shell probe fails, the likely source is local OS policy, endpoint-security software, permissions, or shell configuration. Changing `--profile`, `--unrestricted-paths`, or `--absolute-paths` cannot override either layer.
161
165
 
162
- Do not attempt to evade a host refusal by renaming, encoding, or switching to another arbitrary execution tool. Instead:
166
+ Remote configurable foreground tools advertise an 85-second maximum while preserving each tool’s 30- or 60-second default. Missing or role-hidden tools, non-object arguments, and requests above that limit fail at the shared Worker schema boundary before daemon dispatch; schema failures include `side_effects_started=false`. A legacy client asking for SSE receives the same pre-persistence rejection as a JSON client rather than an allocated recovery stream. Do not treat this as a retry invitation for the same oversized mutation, and do not attempt to evade a host refusal by renaming, encoding, or switching to another arbitrary execution tool. Instead:
163
167
 
164
168
  1. register credentials locally as resource aliases so their values never enter MCP arguments;
165
169
  2. submit a complete owner-authorized `start_job` plan before the workflow depends on later cleanup calls; `stage_job` is only a non-running draft, while an explicit local operator may use `machine-mcp job submit PLAN.json`;
@@ -16,26 +16,31 @@ This document records project-wide decisions that must survive individual fixes,
16
16
  10. **Exclusive claims are complete before visible.** Never create a final lock/PID claim and then populate it. Use the shared exclusive-file primitive, ownership tokens, process-start identity, and snapshot-checked reclamation.
17
17
  11. **Service and state removal are fail-closed state machines.** Stop the platform provider and every verified daemon before removing definitions or recursive state. An unreadable lock, failed stop, active job, or ambiguous identity retains state for diagnosis.
18
18
  12. **Read failure is not empty state.** Permission, type, symbolic-link, size, encoding, and I/O errors must propagate. Corrupt backup/reconstruction applies only after a successful read proves that JSON content is invalid.
19
- 13. **The public protocol contract is current-only.** Shared metadata advertises only the current MCP protocol version. Compatibility code for obsolete protocol dates, lock formats, or state schemas is not retained in the final runtime; upgrade safety comes from explicit version negotiation, fail-closed state validation, and bounded operator convergence.
19
+ 13. **Protocol eras are explicit and non-overlapping.** Shared metadata advertises modern MCP `2026-07-28` as primary and legacy `2025-11-25` only through a named compatibility adapter. Per-request modern metadata must never enter the legacy session/replay machinery, and legacy state must never be inferred for a modern request. Other obsolete protocol dates, lock formats, and state schemas are not retained.
20
20
  14. **Security analysis is a failing gate.** CodeQL or Scorecard execution alone is not success. Generated SARIF must contain no unaccepted result, and missing rule metadata fails closed rather than being interpreted as non-security. An intentional or externally constrained finding requires an exact rule/path record with a substantive rationale and an expiry date.
21
- 15. **Ambiguous health is not permission to repeat a remote write.** A successful Wrangler deployment is recorded before secondary health verification. Timeout, proxy, TLS, network, and temporary service failures preserve the deployment fingerprint and fail for diagnosis; only bounded evidence of a stale identity/version permits automatic same-name redeployment. Changing the Worker name is an explicit remote-resource transition, not a retry strategy.
22
- 16. **Execution continuity and delivery continuity are separate proof obligations.** Keeping work alive after a client transport closes is insufficient unless the same authenticated principal can recover a terminal result or a durable handle. Fresh requests and replay endpoints must remain separate so recovery cannot accidentally duplicate a non-idempotent operation.
21
+ 15. **Ambiguous health is not permission to repeat a remote write.** A successful Wrangler deployment is recorded before secondary health verification. Timeout, proxy, TLS, network, and temporary service failures preserve the deployment fingerprint and fail for diagnosis; only bounded evidence of a stale identity/version permits ordinary automatic same-name redeployment. During owner-authorized candidate activation, an explicit cryptographic device-authentication rejection after current-version health is separate positive evidence that deployment secrets did not converge; it permits one same-name redeployment with the unchanged selected identity, never rotation or resource renaming. Changing the Worker name remains an explicit remote-resource transition, not a retry strategy.
22
+ 16. **Execution continuity and delivery continuity are separate proof obligations.** Legacy transport-surviving work requires authenticated recovery or a durable handle, and fresh requests must remain separate from replay. Modern request-scoped HTTP work instead treats stream closure as cancellation and must not silently continue or enter the legacy replay store.
23
23
  17. **Remote compound commands are not persistence evidence.** When a remote edit and a long test share one relay call, a transport interruption can obscure whether the edit completed. High-impact writes must be followed by an independent read of stable anchors or a Git diff before tests and conclusions rely on them.
24
- 18. **Durable state owners do not retain cross-event terminal Promises or depend on JavaScript timers as durable ownership.** A Durable Object that must accept cancellation, status, recovery, or hibernation cannot retain the public SSE response, an internal request waiting for completion, or an unresolved Promise owned by the initiating fetch event. The outer Worker owns streaming. Stream initiation transactionally persists the stream plus daemon-call ownership, connection generation, operation deadline, reconnect deadline, request correlation, and bounded result-transform metadata before sending work. Later WebSocket, cancellation, timeout, send-failure, or reconnect-expiry events converge through one guarded terminal write. Durable Object alarms and compensating event-entry sweeps own cross-event deadlines; JavaScript timers remain only for bounded JSON-response calls. Descriptor requests remain short; terminal delivery uses one authenticated hibernatable WebSocket subscription. Both are admitted only on the internal service-binding path and are unreachable through caller-supplied internal headers.
24
+ 18. **Durable state owners do not retain cross-event terminal Promises or depend on JavaScript timers as durable ownership.** Legacy recoverable stream initiation validates the call, then transactionally persists stream and daemon-call ownership before dispatch; later events converge through one guarded terminal write, with alarms and event-entry sweeps owning deadlines. Its descriptor and hibernatable subscription paths are internal-only. Modern streams are different: one Durable Object fetch owns the direct response, the outer Worker keeps it observable with bounded SSE comments, and a random private capability indexes only the already-active pending call. Public control headers are stripped, cancellation carries no OAuth/DPoP credential, and modern code must never add prepare/subscribe descriptors, terminal-result retention, or a cross-event Promise registry.
25
+ 19. **Validation cost is part of the input contract.** Schema compilation and runtime validation have independent depth/node/pattern/issue/work budgets. Every traversed array item and own object property consumes work before recursive validation; code must not allocate an unbounded key/value inventory and only then check limits.
26
+ 20. **A pending release candidate is valid only against the current packaged source.** Candidate start/activation must compare package identity and promotion digest before tarball verification, installation, deployment, or service mutation. Tarball-to-manifest integrity alone is insufficient after later edits.
27
+ 21. **GitHub publication requires an explicit owner-terminal ceremony and one process owner.** Candidate activation, acceptance, merge, and green CI are evidence, not standing permission to create a tag or Release. Publication must present real TTY streams plus the explicit confirmation flag and hold the repository publication lock through tag/Release synchronization. Background agents and managed jobs may verify state but may not publish. This is a workflow safeguard against accidental or ordinary non-interactive publication, not cryptographic human-presence proof against arbitrary code already running as the same OS user.
28
+ 22. **Capability routing advises; effective policy authorizes.** The resolver may shortlist tool sets, report ambiguity, and suggest fallbacks, but it must neither hide an allowed escape hatch nor recommend a tool outside the request's effective account/daemon intersection. Canonical full continues to expose direct Bash. A routing fallback is not a safety-policy bypass.
29
+ 23. **Capability fingerprints reduce repetition, not freshness checks.** A matching client-supplied fingerprint may omit unchanged static instructions, but task-specific ranking, application discovery, and route computation must still run. The fingerprint is not conversation identity, authorization, or a cached execution result.
25
30
 
26
31
  A proposed change that conflicts with an invariant requires an explicit owner decision and corresponding documentation update. It must not be hidden inside an unrelated refactor.
27
32
 
28
33
  ## Change and release-operation ownership
29
34
 
30
- Repository implementation, candidate preparation, observed live verification, acceptance recording, source pull requests, tags, and GitHub Releases are distinct from owner-operated live activation and npm publication.
35
+ Repository implementation, candidate preparation, observed live verification, acceptance recording, and source pull requests are distinct from owner-operated live activation, GitHub tag/Release publication, and npm publication.
31
36
 
32
37
  For each package change, automation prepares the exact prerelease tarball and stops. The owner executes `npm run release:candidate:activate -- --allow-worker-deploy`. After that command updates the Worker, verifies candidate relay readiness, replaces the login daemon, and verifies service handoff, the coding agent inspects the connected system through Machine Bridge. Only then may it record candidate acceptance and push through `npm run github:push`.
33
38
 
34
- After merge and exact-commit checks, automation creates a GitHub Prerelease with `npm run prerelease:release`. The owner explicitly publishes npm with `npm run prerelease:publish` and activates the registry package with `npm run prerelease:install -- --allow-worker-deploy`. The owner uses the prerelease for the required interval and explicitly reports whether blocking issues remain. Automation must not infer soak success from elapsed time.
39
+ After merge and exact-commit checks, automation may prepare and verify the release state, but GitHub tag/Release publication is an explicit owner-terminal operation: `npm run prerelease:release -- --owner-terminal-confirm`. The command rejects background jobs, MCP calls, CI, redirected sessions, and concurrent publication. The owner separately publishes npm with `npm run prerelease:publish` and activates the registry package with `npm run prerelease:install -- --allow-worker-deploy`. The owner uses the prerelease for the required interval and explicitly reports whether blocking issues remain. Automation must not infer soak success from elapsed time.
35
40
 
36
- Stable promotion is content-preserving. `release:soak:verify` compares the packaged functional digest with the accepted prerelease. A mismatch requires another prerelease and a restarted soak. After stable candidate activation and observed verification, automation may complete `npm run release`; the owner separately authorizes `npm run stable:publish`.
41
+ Stable promotion is content-preserving. `release:soak:verify` compares the packaged functional digest with the accepted prerelease. A mismatch requires another prerelease and a restarted soak. After stable candidate activation and observed verification, the owner creates the final GitHub tag and Release from an interactive terminal with `npm run release -- --owner-terminal-confirm`; the owner separately authorizes `npm run stable:publish`.
37
42
 
38
- Live npm publication, global installation, Worker/service replacement, credential mutation, and unrelated live-state changes remain explicit owner decisions. `npm run release` never pushes `main`.
43
+ GitHub/npm publication, global installation, Worker/service replacement, credential mutation, and unrelated live-state changes remain explicit owner decisions. GitHub release commands never push `main`.
39
44
 
40
45
  ## Default instruction invariant
41
46
 
@@ -77,6 +82,23 @@ Rules:
77
82
 
78
83
  `runtime.mjs` owns local tool semantics. `relay-connection.mjs` owns authenticated relay connection lifecycle. The CLI orchestrates them; it must not become the second implementation of either.
79
84
 
85
+ ## MCP and tool-schema contract
86
+
87
+ MCP protocol-era selection is an adapter concern. Domain execution must not depend on connection history, an HTTP socket, or a process as conversation identity. Modern requests are interpreted solely from their per-request metadata; legacy session state must stay in legacy modules and may not leak into modern request keys, cancellation, caching, or result framing.
88
+
89
+ The shared tool catalog is executable schema:
90
+
91
+ - absent `$schema` means JSON Schema 2020-12;
92
+ - only the explicitly implemented bounded keyword set may be used;
93
+ - unsupported dialects/keywords fail process/module initialization rather than being ignored;
94
+ - external `$ref` values are rejected and never fetched;
95
+ - schema depth, total nodes, regular-expression length, and returned issue count are bounded;
96
+ - Worker validation occurs before daemon dispatch, and local validation remains defense in depth for every transport;
97
+ - validation errors contain paths and constraints, never argument values;
98
+ - a schema or validation change requires direct validator tests plus Worker and stdio integration when observable protocol behavior changes.
99
+
100
+ `structuredContent` is arbitrary JSON, not object-only. Code must distinguish absence from the valid value `null`; truthiness is not a presence check for structured protocol fields.
101
+
80
102
  ## Logging contract
81
103
 
82
104
  Operational logs are a user interface, not a dump of protocol events.
@@ -153,7 +175,7 @@ The required matrix includes:
153
175
  - stdio JSON-RPC integration;
154
176
  - Worker OAuth/WebSocket/MCP integration;
155
177
  - managed-job integrity, recovery, cancellation, cleanup, and redaction;
156
- - dependency audit, registry signatures/attestations, SBOM, and Worker dry run.
178
+ - dependency audit, registry signatures/attestations, the first-party `sbom:test` CycloneDX identity/graph/privacy check, and Worker dry run.
157
179
 
158
180
  Cross-platform tests must not depend on shell syntax, case-sensitive Windows paths, Unix-only executable shims, or timing races when a deterministic scheduler can be injected. Local success cannot substitute for the required Linux/macOS/Windows push CI result used by the release gate.
159
181
 
@@ -93,9 +93,11 @@ Menu-bar and menu subtrees are not recursively expanded by default. This keeps m
93
93
 
94
94
  ## Capability discovery and automatic selection
95
95
 
96
- `resolve_task_capabilities` rescans instruction files, skills, explicit/automatic package commands, and relevant local automation metadata on every call. It ranks matching skills and commands, optionally loads the best skill, and compares every canonical-full task with cached installed-application names, so a task that directly names an app does not need generic “app/window” wording. Application inventory is refreshed after a bounded cache interval. Per-root discovery failures are returned as bounded `warnings`, and capability resolution reports `application_discovery.available`, warning count, truncation, and a coarse error class instead of silently treating an unreadable inventory as an empty successful scan.
96
+ `resolve_task_capabilities` rescans instruction files, skills, explicit/automatic package commands, policy-visible tool definitions, and relevant local automation metadata on every call. It ranks matching skills and commands, optionally loads the best skill, and returns set-level route advice across direct Bash/argv, process sessions, managed jobs, files/Git, browser, applications, protected resources, and diagnostics. This is not a restriction layer: `exec_command` remains the general shell escape hatch under an effective shell-capable policy.
97
97
 
98
- This is the strongest reliable server-side automation boundary available through MCP: discovery, refresh, ranking, and progressive skill loading are automatic. The MCP host still owns the model loop and decides whether a recommended tool is exposed, approved, or invoked. Machine Bridge cannot force ChatGPT web or another host to make a call that the host declines.
98
+ Application inventory is consulted only when the request's effective account/daemon policy permits application discovery, then cached briefly and refreshed after a bounded interval. A task that directly names an installed app does not need generic “app/window” wording. Per-root discovery failures are returned as bounded `warnings`, and capability resolution reports `application_discovery.available`, warning count, truncation, and a coarse error class instead of silently treating an unreadable inventory as an empty successful scan. A matching `known_refresh_fingerprint` can omit unchanged static context without skipping the fresh capability scan.
99
+
100
+ This is the strongest reliable server-side automation boundary available through MCP: discovery, refresh, ranking, route-set construction, and progressive skill loading are automatic. The MCP host still owns the model loop and decides whether a recommended tool is exposed, approved, or invoked. Machine Bridge cannot force ChatGPT web or another host to make a call that the host declines.
99
101
 
100
102
  ## Security model
101
103