machine-bridge-mcp 3.0.0-beta.28 → 3.0.0-beta.30

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 (50) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/browser-extension/manifest.json +1 -1
  3. package/docs/ARCHITECTURE.md +5 -5
  4. package/docs/AUDIT.md +26 -0
  5. package/docs/LOGGING.md +3 -3
  6. package/docs/OPERATIONS.md +6 -6
  7. package/docs/TESTING.md +3 -3
  8. package/docs/THREAT_MODEL.md +2 -2
  9. package/docs/TOOL_REFERENCE.md +33 -32
  10. package/docs/UPGRADING.md +7 -1
  11. package/package.json +1 -1
  12. package/scripts/coverage-check.mjs +1 -0
  13. package/src/local/owner-state-lock.mjs +18 -12
  14. package/src/local/process-execution.mjs +5 -10
  15. package/src/local/process-foreground-timeout.mjs +28 -0
  16. package/src/local/runtime-tool-handlers.mjs +1 -1
  17. package/src/local/security-audit-log.mjs +5 -2
  18. package/src/local/security-audit-state.mjs +139 -0
  19. package/src/local/security-audit-storage.mjs +88 -153
  20. package/src/local/security-audit-worker.mjs +4 -5
  21. package/src/shared/foreground-timeout.d.mts +10 -0
  22. package/src/shared/foreground-timeout.mjs +42 -0
  23. package/src/shared/tool-catalog.json +33 -32
  24. package/src/worker/dpop.ts +73 -2
  25. package/src/worker/index.ts +68 -68
  26. package/src/worker/mcp-access.ts +5 -2
  27. package/src/worker/mcp-legacy-stream-prepare.ts +145 -0
  28. package/src/worker/mcp-pending-call-records.ts +17 -0
  29. package/src/worker/mcp-pending-call-store.ts +14 -3
  30. package/src/worker/mcp-request-fingerprint.ts +24 -0
  31. package/src/worker/mcp-resumption-records.ts +26 -2
  32. package/src/worker/mcp-resumption-request-index.ts +72 -0
  33. package/src/worker/mcp-resumption.ts +11 -20
  34. package/src/worker/mcp-stream-attempt.ts +27 -0
  35. package/src/worker/mcp-stream-channel.ts +42 -28
  36. package/src/worker/mcp-stream-dispatch.ts +2 -0
  37. package/src/worker/mcp-stream-prepare-retry.ts +47 -0
  38. package/src/worker/mcp-stream-proxy-contract.ts +13 -0
  39. package/src/worker/mcp-stream-proxy.ts +33 -4
  40. package/src/worker/mcp-stream-subscription.ts +49 -64
  41. package/src/worker/mcp-stream-terminal-socket.ts +72 -0
  42. package/src/worker/mcp-stream.ts +5 -3
  43. package/src/worker/nonce-store.ts +17 -0
  44. package/src/worker/observability.ts +13 -3
  45. package/src/worker/tool-timeout.ts +10 -23
  46. package/src/worker/worker-edge-guard.ts +10 -1
  47. package/src/worker/worker-entry.ts +4 -1
  48. package/src/worker/worker-rate-limit-key.ts +30 -0
  49. package/src/worker/worker-runtime-config.ts +1 -0
  50. package/wrangler.jsonc +5 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.0.0-beta.30 - 2026-08-02
4
+
5
+ ### Resumable MCP delivery under transient interruption
6
+
7
+ - Make the advertised and executed foreground timeout contract match the enforced Worker ceiling: configurable foreground tools now declare a maximum of 85 seconds and default to 30 or 60 seconds. Relay execution uses those same defaults when the argument is omitted, and a registered-command manifest cannot silently extend a relay call beyond 85 seconds; owner-local registered commands may retain their explicit local manifest budget. Longer remote work must use process sessions or managed jobs, eliminating host-generated or locally inherited 120–600 second work that outlived its Worker response.
8
+ - Stop legacy recovery subscribers from replacing one another. Up to four concurrent subscribers may observe the same persisted terminal result; excess subscribers receive a bounded retryable response, and terminal fan-out closes every subscriber cleanly.
9
+ - Extend internal terminal-subscription recovery from a sub-second retry burst to a bounded multi-second backoff. Cancelling a public SSE reader now releases only its internal delivery subscription while the durable legacy operation remains resumable through `Last-Event-ID`. DPoP-protected prepare retries use one outer-Worker-generated opaque retry ID: the first attempt atomically consumes the proof and binds it, and only the same internal request may reuse that proof for at most four authorization attempts; another request remains a replay failure.
10
+ - Make repeated signed-session legacy `tools/call` delivery idempotent throughout the bounded two-minute recovery window. OAuth token, signed MCP session, typed request ID, tool name, and a canonical SHA-256 argument fingerprint bind the stream before daemon dispatch; an identical retry reattaches to the active or terminal stream, while changed arguments are rejected instead of duplicating side effects. Sessionless legacy POSTs never retry an ambiguous prepare.
11
+ - Add regressions for concurrent subscriber fan-out and limits, delivery-subscription cleanup, canonical request fingerprints, persisted retry identity, the unified foreground timeout catalog, and effective relay timeout alignment for shell, direct-process, and registered-command execution. Clarify that a macOS sleep interval may legitimately surface as an event-loop-stall warning without implying daemon failure.
12
+
13
+ ## 3.0.0-beta.29 - 2026-08-01
14
+
15
+ ### Bounded security-audit throughput and retention
16
+
17
+ - Reuse one verified security-audit state inside the dedicated audit worker instead of rereading, reparsing, and rehashing the complete retained chain for every batch. The cache is invalidated by file identity, size, modification time, or metadata-change time, so another process or external alteration still forces full verification before a write.
18
+ - Bound retention by both 4,096 events and 4 MiB. Oversized-but-valid event histories now evict the oldest events, advance the chain anchor, and remain verifiable instead of permanently failing before the advertised event limit. Runtime diagnostics expose the byte ceiling explicitly.
19
+ - Fix an owner-state-lock race where a contender observed `EEXIST` just before the holder released the lock and then misclassified the now-missing file as malformed. Missing, invalid, and valid-owner states are now distinct, preserving fail-closed handling for actual corruption while allowing normal retry.
20
+ - Add regressions for cached-state tamper invalidation, byte-driven retention, cross-worker sequence preservation, and the lock release/acquire window. Keep audit state construction in a focused module rather than raising the existing architecture budget.
21
+ - Mark Worker observability counters as current-isolate metrics and state explicitly that durable calls can cross isolate lifetimes, so completed/failed counts are not misread as algebraically closed process-lifetime totals.
22
+
3
23
  ## 3.0.0-beta.28 - 2026-07-31
4
24
 
5
25
  ### Verified service restart semantics
@@ -30,6 +30,6 @@
30
30
  "action": {
31
31
  "default_title": "Machine Bridge Browser"
32
32
  },
33
- "version_name": "3.0.0-beta.28",
33
+ "version_name": "3.0.0-beta.30",
34
34
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxryYkpZhq8+VAQLHcGS9BAHQcyKX8RHGIpIwvtIVRU/rcOcE0bNdnM0aZJ/h6xWQsGDHlhvjT2+1aJaAn/9k8473BRWajzVXld961CdHYVFVHoce2hHiSJ0xydWrHMMZhAm0mN0UzjEpgZ0tMw209efcZHIvSwuxhteZMRy4kyiVjwFlOf5oXFCxRuCJnPj3AK9CmCf4XgEBuPIJ0TZmjGHOOdBvJmbCNnAWXYEo5/mf7MfCGhV4IJ1hNuhpoNQfOFKMUcw9/v/IpT62XpfXdGYTfGYCmCjC+gntK1spbkr2P4/2+sYMQtLpse71mpSNGXfcf3abU55Vpn+gncSxRQIDAQAB"
35
35
  }
@@ -126,13 +126,13 @@ Public `/healthz`, `/`, discovery metadata, CORS preflight, and unknown-path 404
126
126
  - a bounded persistent index for legacy streamed daemon-call ownership, opaque connection generation, request correlation, result-transform metadata, and monotonic operation/reconnect deadlines;
127
127
  - bounded legacy resumable MCP delivery metadata and terminal responses for recently disconnected legacy SSE clients.
128
128
 
129
- `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.
129
+ `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. The outer Worker applies a two-level stateful burst guard before Durable Object routing: a high-capacity route/Worker bucket preserves aggregate abuse resistance, while a lower route/subject bucket isolates authenticated credentials or anonymous network identities through internal truncated SHA-256 keys. Raw credentials and addresses are never stored in application logs or returned by diagnostics; the hash is an opaque bucketing key, not a claim of cryptographic anonymity for low-entropy network addresses. Limiter failure is fail-open and never logs the key material. 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.
130
130
 
131
- 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.
131
+ 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`, `mcp-request-fingerprint.ts`, and `durable-stream-calls.ts` own the MCP `2025-11-25` signed-session, persistent-call, recovery-GET, and `Last-Event-ID` contract. Active request identity includes a canonical argument fingerprint, so a lost prepare response can be retried and reattached without repeating side effects. Up to four internal delivery subscribers may coexist and receive the same terminal result; subscriber admission is serialized, excess receivers are retryable, and closing one public reader releases only that delivery socket. `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.
132
132
 
133
133
  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.
134
134
 
135
- 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.
135
+ Legacy MCP `2025-11-25` requests use the compatibility adapter. It issues a signed `Mcp-Session-Id` and treats token + signed session + typed JSON-RPC ID as a bounded idempotency and cancellation domain. Tool name plus a canonical argument fingerprint is persisted before daemon dispatch. During the two-minute recovery window, an identical signed-session POST reattaches to the active or terminal stream; the same ID with changed arguments is rejected because prior side effects may have started. 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 client that intentionally starts a new legacy operation must use a fresh typed request ID until the prior recovery record expires or sequence one is explicitly acknowledged, which atomically removes the replay record. Sessionless legacy requests intentionally remain independent because token-wide IDs are not a safe client identity; such clients have no POST idempotency guarantee, and the outer Worker does not automatically retry an ambiguous sessionless prepare. They should establish a signed session before side-effecting calls. A signed-session prepare protected by DPoP receives a fresh internal `retry_*` identifier generated after public headers are sanitized. Its proof JTI and retry identifier are atomically bound in Durable Object storage. At most four internal authorization attempts with the same binding are accepted, but a different outer request, a direct client replay, an invalid internal identifier, or inconsistent nonce/binding state is rejected. A Durable Object restart may rediscover a persisted legacy call; this recovery machinery is never consulted by the modern dispatcher.
136
136
 
137
137
  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.
138
138
 
@@ -197,7 +197,7 @@ Remote OAuth binds each code, access token, and refresh token to a named Machine
197
197
  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.
198
198
  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.
199
199
 
200
- 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.
200
+ 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 idempotency, replay, and cancellation are scoped to OAuth token + signed MCP session + typed JSON-RPC ID for the bounded recovery lifetime; intentional new work uses a fresh ID during that interval. Stdio has one process-local in-flight ID index because all requests share one explicit transport channel.
201
201
 
202
202
  ## Stdio request lifecycle
203
203
 
@@ -293,7 +293,7 @@ Browser-origin handling separates CORS response sharing from protocol authentica
293
293
 
294
294
  ## Observability
295
295
 
296
- 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.
296
+ 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 canonical MCP catalog advertises one foreground timeout contract of 1–85 seconds with tool-specific 30- or 60-second defaults, and the Worker rejects larger values before any daemon message is sent. `foreground-timeout.mjs` is the shared source for those defaults and limits; `process-foreground-timeout.mjs` applies them again at the local relay execution boundary so omitted values and registered-command manifests cannot outlive the Worker response. Owner-local registered commands may still use their explicit local manifest timeout. Longer remote work uses process sessions or managed jobs rather than a synchronous foreground response. `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.
297
297
 
298
298
  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.
299
299
 
package/docs/AUDIT.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Security and privacy audit notes
2
2
 
3
+ ## 2026-08-02 version 3.0.0-beta.30 interruption and recovery review
4
+
5
+ A repeated-call incident was reproduced while the local launchd daemon remained the same healthy beta.29 process. Recent security-audit records showed that many 20–78 second local calls reached terminal state even when the MCP host reported interruption, and the public MCP endpoint was briefly unreachable while the daemon connection identity remained unchanged. Live Cloudflare sampling after recovery showed no Worker exceptions; internal legacy subscription WebSockets closed frequently as part of their ordinary terminal lifecycle. The evidence therefore separates three cases: local execution failure, Worker/DO delivery interruption after admission, and a connection failure before Worker code runs. Only the first two are observable or repairable inside this repository; a pre-Worker edge/TCP/TLS failure still requires host retry or an independently operated alternate endpoint.
6
+
7
+ The review found five concrete amplification defects. The shared tool catalog advertised 120-second defaults and a 600-second maximum while the Worker rejected values above 85 seconds, allowing the host to generate requests that could never pass remote validation. After the catalog was corrected, the full gate exposed the inverse execution mismatch: `exec_command` and `run_process` still defaulted to 120 seconds locally when relay arguments omitted the field, and `run_local_command` could inherit a 600-second owner manifest even though the Worker waited only 60 or 85 seconds. Legacy internal subscription recovery retried only over roughly 400 milliseconds and every new resume forcibly closed the previous subscriber, allowing concurrent original/resume paths to replace each other. A lost prepare response caused the outer Worker to repeat no request, while a client retry had no explicit argument fingerprint for safe in-flight reattachment. Finally, the stateful burst limiter keyed only by Worker hostname, so every account and client shared one 120-request/minute bucket.
8
+
9
+ Beta.30 makes the foreground catalog truthful at its source: every configurable synchronous tool advertises a maximum of 85 seconds and a 30- or 60-second default on every MCP surface. A shared timeout module now drives both Worker admission and local relay execution; omitted relay values use the advertised default, and registered-command manifests are capped at the remote ceiling before process spawn. Owner-local registered-command invocation remains allowed to use its explicit local manifest budget. Longer remote work uses process sessions or managed jobs. Signed-session legacy preparation now retries a small bounded set of transient DO failures with an exact cloned request; token, signed session, typed JSON-RPC ID, tool name, and a canonical SHA-256 argument fingerprint bind the stream before daemon dispatch and remain through its bounded terminal-retention window, so an identical retry reattaches and changed arguments fail rather than duplicating side effects. Terminal subscriptions retry over a bounded multi-second window, permit four simultaneous receivers, multicast one persisted terminal result, and release only the delivery socket when a public reader disappears. A serialized subscriber-admission gate prevents concurrent limit bypass. Sessionless legacy requests remain independent by design because a shared bearer token and typed ID cannot safely identify one client; the exact-POST reattachment guarantee therefore requires the signed session issued by legacy initialization. Sessionless POST preparation is attempted once so an ambiguous response cannot cause an automatic duplicate side effect. For DPoP-bound access tokens, the outer Worker replaces any public internal header with a fresh random retry ID. The Durable Object transaction atomically consumes the proof and stores `proof replay key -> retry ID`; only that same opaque internal request may reuse the proof for at most four authorization attempts, and the binding is accepted only while the primary nonce record remains present. Different retry IDs, malformed IDs, missing nonce state, and ordinary client replay fail closed.
10
+
11
+ The burst guard is now layered: a 1,200-request/minute route/Worker bucket limits aggregate abuse, while a 120-request/minute route/subject bucket isolates authenticated credentials or an anonymous network identity through a truncated SHA-256 digest. Raw credentials, addresses, request keys, arguments, and results are never used as log fields. Limiter failure remains fail-open so the optional guard cannot become a control-plane outage.
12
+
13
+ A separate 100-second `runtime.event_loop.stall` warning was correlated exactly with macOS sleep from 07:15:31 to the 07:17:40 wake. The unchanged daemon PID and power-management log prove that warning was correct sleep/wake accounting rather than JavaScript blockage. Sleep can still interrupt network transport; same-daemon relay recovery remains bounded by the existing reconnect contract.
14
+
15
+ ## 2026-08-01 version 3.0.0-beta.29 security-audit storage review
16
+
17
+ Review of the live 4,096-event audit chain found that moving persistence to a worker thread removed daemon event-loop blocking but did not remove storage amplification. Each sparse batch still read roughly 3 MiB, parsed the complete document, recalculated every SHA-256 link, serialized the complete state, atomically replaced it, and called `fsync`. On the inspected machine, a cold full-chain verification took approximately 1.3 seconds. The previous implementation therefore could accumulate audit work at ordinary interactive call rates even though tool-result delivery itself remained asynchronous.
18
+
19
+ Beta.29 gives each audit worker a verified storage session. The first access performs the complete bounded read and chain verification. Subsequent writes reuse that state only while the on-disk regular-file identity, size, modification time, and metadata-change time remain unchanged. A cross-process writer or external alteration changes that identity and forces a fresh secure read and full hash-chain verification under the owner-state lock. The cache never turns an unverifiable file into an accepted state, and a failed write does not advance the cached state. In the same environment, cached single-record writes fell to roughly 39–264 milliseconds; the remaining cost is full-file serialization, atomic replacement, and `fsync`, not repeated chain verification. A segmented append/checkpoint format remains a possible future optimization if sustained high-rate audit throughput becomes a product requirement.
20
+
21
+ The review also proved that the former retention contract was internally inconsistent. Every event field could be within its legal bound while 4,096 events exceeded the separate 4 MiB file ceiling. The storage layer then rejected the write and left the audit backend unhealthy before reaching its advertised count limit. Retention is now the intersection of the event and byte limits: event encodings are measured exactly, the oldest prefix is removed until both bounds hold, and the anchor advances to the last removed hash so the retained suffix verifies independently. Diagnostics expose `maximum_bytes` alongside the event maximum.
22
+
23
+ Faster writes exposed a pre-existing lock race. After exclusive creation failed with `EEXIST`, a holder could release the lock before the contender read it. The contender treated both `ENOENT` and malformed content as corruption. The lock reader now returns distinct missing, invalid, and valid-owner states; only missing retries, while malformed state continues to fail closed. Cross-worker audit tests exercise this release/acquire window and verify continuous sequence numbers with no lost events.
24
+
25
+ The live review also showed isolate-local event counters that could appear inconsistent with the persistent pending-call snapshot after an isolate restart. That is not durable-call loss: a persisted call may start in one isolate and complete in a later isolate. Beta.29 adds an explicit machine-readable `metric_scope` declaring the current-isolate lifetime and non-closed counter algebra; the persistent pending-call snapshot remains authoritative for active ownership.
26
+
27
+ This source review does not activate beta.29, replace the running beta.28 daemon, deploy the Worker, modify live audit or legacy authorization files, publish npm, create a tag, or record acceptance.
28
+
3
29
  ## 2026-07-31 version 3.0.0-beta.27 control-plane resilience audit
4
30
 
5
31
  A host-pressure incident exposed a causal chain that ordinary timeout tests had not covered: a metadata-heavy directory deletion exceeded its foreground deadline; subsequent minimal calls were delayed; and the daemon later classified the relay as unresponsive. Review of production diagnostics and the exact installed source showed that host filesystem/endpoint-security load was the trigger, but Machine Bridge amplified it. Timeout/cancellation performed synchronous process-table snapshots on the daemon thread, every remote call synchronously reread, rehashed, rewrote, and `fsync`ed the complete bounded audit file before returning a result, the process could remain alive after the timeout response, and heartbeat policy could not distinguish remote silence from a locally stalled event loop. Existing tests proved that `cancel_call` was sent and the Promise rejected; they did not prove post-timeout control-plane availability or completed process drain.
package/docs/LOGGING.md CHANGED
@@ -12,7 +12,7 @@ Logs should answer:
12
12
  4. Is an infrastructure, protocol, deployment, or local service problem requiring action?
13
13
  5. When debug logging is explicitly enabled, which bounded implementation event should be correlated?
14
14
 
15
- Logs are not a command history or content transcript. The local security audit provides a bounded SHA-256 hash chain over coarse operation metadata without recording command text, paths, contents, form values, or output. Audit inputs are projected to an allowlist before crossing to a dedicated Worker thread; chain verification, batching, atomic replacement, and `fsync` never delay tool-result delivery on the daemon event loop. Remote `diagnose_runtime.runtime.security_audit` reports worker readiness, queue depth/capacity, dropped records, retained entries, and chain health; local stdio exposes the same snapshot as `server_info.security_audit`. Before the Worker reports readiness, health is explicitly `audit_initializing`; the daemon thread does not synchronously read the chain. The queue is not a write-ahead log: a process or operating-system crash before persistence can lose queued events, and `dropped_records` counts only failures observed during the current process lifetime. The chain detects local alteration but is not a remote immutable ledger and is not a substitute for OS isolation.
15
+ Logs are not a command history or content transcript. The local security audit provides a SHA-256 hash chain over coarse operation metadata without recording command text, paths, contents, form values, or output. Retention is bounded by both 4,096 events and 4 MiB; byte-driven eviction advances the chain anchor so the retained suffix remains verifiable, and diagnostics expose both ceilings. Audit inputs are projected to an allowlist before crossing to a dedicated Worker thread; batching, atomic replacement, and `fsync` never delay tool-result delivery on the daemon event loop. The audit worker verifies the complete chain on first access and reuses that state only while the regular-file identity and timestamps remain unchanged. Cross-process writes or external alteration force a secure reread and complete verification before another write. Remote `diagnose_runtime.runtime.security_audit` reports worker readiness, queue depth/capacity, dropped records, retained entries, event/byte limits, and chain health; local stdio exposes the same snapshot as `server_info.security_audit`. Before the Worker reports readiness, health is explicitly `audit_initializing`; the daemon thread does not synchronously read the chain. The queue is not a write-ahead log: a process or operating-system crash before persistence can lose queued events, and `dropped_records` counts only failures observed during the current process lifetime. The chain detects local alteration but is not a remote immutable ledger and is not a substitute for OS isolation.
16
16
 
17
17
  ## Levels
18
18
 
@@ -63,11 +63,11 @@ Brief network interruptions are expected on laptop network changes, Worker deplo
63
63
  - a verified replacement is a distinct warning and permanently stops the older daemon;
64
64
  - failure to receive `hello_ack` within the handshake deadline, or `ready_ack` within the independent end-to-end readiness deadline, terminates the candidate socket and retries;
65
65
  - lack of inbound heartbeat activity terminates a half-open socket and reconnects;
66
- - a late local heartbeat tick is classified as `runtime.event_loop.stall`, sends a fresh probe, and defers disconnect for a bounded recovery interval instead of being mislabeled as immediate remote failure.
66
+ - a late local heartbeat tick is classified as `runtime.event_loop.stall`, sends a fresh probe, and defers disconnect for a bounded recovery interval instead of being mislabeled as immediate remote failure; a macOS sleep/wake interval may legitimately produce this warning without a daemon fault.
67
67
 
68
68
  A WebSocket close code such as `1006` means the transport ended without a normal close handshake. It is useful for debug diagnosis but not useful as the default user message. It is not evidence that the daemon process restarted. Worker `daemon_transport_error` / `daemon_liveness_timeout` messages and their 1012 close frames are likewise retryable connection conditions, not upgrade instructions. Only an unknown/incompatible Worker error, authentication failure, or identity/version mismatch may produce the fatal protocol/configuration log and daemon exit. Default logs therefore describe the affected layer, duration, classification, and recovery behavior rather than printing raw close envelopes.
69
69
 
70
- Streamed-call diagnostics are deliberately coarse. Modern request-scoped stream ownership is memory-only; legacy MCP `2025-11-25` may additionally report aggregate persistent active/detached counts, oldest age, tool-name counts, alarm mutations, unmatched-result counts, and whether a legacy call is transient or durable. Logs and `server_info` must not include tool arguments, terminal results, command text, request keys, account identifiers, raw call IDs, raw connection generations, mirrored parameter values, private paths, or subscriber payloads. A stale-generation result is counted as unmatched rather than logged with its envelope.
70
+ Streamed-call diagnostics are deliberately coarse. Modern request-scoped stream ownership is memory-only; legacy MCP `2025-11-25` may additionally report aggregate persistent active/detached counts, oldest age, tool-name counts, alarm mutations, unmatched-result counts, opened/coexisting/limited delivery-subscriber counts, and whether a legacy call is transient or durable. Worker event counters are scoped to the current isolate and say so in `metric_scope`; persisted durable calls can begin in one isolate and complete in another, so `started`, `completed`, and `failed` are not algebraically closed process-lifetime totals. The persistent pending-call snapshot is authoritative for current ownership. Logs and `server_info` must not include tool arguments, terminal results, command text, request keys, account identifiers, raw call IDs, raw connection generations, mirrored parameter values, private paths, or subscriber payloads. A stale-generation result is counted as unmatched rather than logged with its envelope.
71
71
 
72
72
  Examples:
73
73
 
@@ -59,17 +59,17 @@ A successful diagnostic result applies only to that probe. An MCP host can still
59
59
 
60
60
  ### Concurrent chat windows and pending calls
61
61
 
62
- Machine Bridge supports concurrent calls: the Worker admits 32 pending daemon calls (30 ordinary plus two reserved control calls), and the local runtime admits 16 active tool calls (14 ordinary plus two reserved control calls). The same `diagnose_runtime`/`list_roots` control set is enforced at both layers. These are capacity limits, not a single global execution queue. Modern MCP `2026-07-28` HTTP requests are independent: JSON-RPC IDs are scoped to each request/response stream, so separate clients may reuse the same numeric ID even when they share one OAuth account and token. Legacy MCP `2025-11-25` initialization still receives a signed `Mcp-Session-Id`; duplicate detection, explicit cancellation, and replay for that compatibility path remain session-scoped.
62
+ Machine Bridge supports concurrent calls: the Worker admits 32 pending daemon calls (30 ordinary plus two reserved control calls), and the local runtime admits 16 active tool calls (14 ordinary plus two reserved control calls). The same `diagnose_runtime`/`list_roots` control set is enforced at both layers. These are capacity limits, not a single global execution queue. Modern MCP `2026-07-28` HTTP requests are independent: JSON-RPC IDs are scoped to each request/response stream, so separate clients may reuse the same numeric ID even when they share one OAuth account and token. Legacy MCP `2025-11-25` initialization still receives a signed `Mcp-Session-Id`; idempotency, explicit cancellation, and replay for that compatibility path remain session-scoped. Within the bounded two-minute recovery window, a typed request ID denotes one operation and must not be intentionally reused for new work.
63
63
 
64
64
  `server_info.worker.pending_calls` reports `active`, `detached`, `request_keys`, `maximum`, `oldest_ms`, `by_tool`, `transient`, and `durable_streams`. `worker.sockets_live` separately reports `authenticated`, `probing`, `ready`, and `candidates`; only `ready` sockets contribute to `daemon.connected` and `authorization.effective_tools`. A nonzero `active` count means work is in flight, not that the bridge is globally locked. `detached > 0` means the daemon WebSocket was lost and calls are inside the bounded same-daemon reconnect interval. This relay-layer state exists below both MCP eras.
65
65
 
66
- For modern MCP `2026-07-28`, the public response stream is the request owner: closing it cancels the transient pending call, and no request-key or replay record should remain. For legacy MCP `2025-11-25`, the signed session and typed JSON-RPC ID own duplicate detection and explicit `notifications/cancelled`; closing a resumable public stream alone does not cancel the operation. Legacy terminal completion, explicit cancellation, timeout, or reconnect-grace expiry must eventually return active/detached/request-key counts to zero. A verified same-daemon replacement may reclaim detached relay calls after readiness, while a new daemon process cannot. Delayed results from the old socket are rejected. `detached > 0` materially beyond the two-minute grace, a modern transient call surviving response closure, or a legacy request-key count remaining after active calls reach zero is a lifecycle defect.
66
+ For modern MCP `2026-07-28`, the public response stream is the request owner: closing it cancels the transient pending call, and no request-key or replay record should remain. For legacy MCP `2025-11-25`, the signed session and typed JSON-RPC ID own bounded idempotency and explicit `notifications/cancelled`; closing a resumable public stream alone does not cancel the operation. Legacy terminal completion, explicit cancellation, timeout, or reconnect-grace expiry must eventually return active/detached/pending-call request-key counts to zero, while the separate stream-level replay identity may remain until the two-minute recovery record expires. A verified same-daemon replacement may reclaim detached relay calls after readiness, while a new daemon process cannot. Delayed results from the old socket are rejected. `detached > 0` materially beyond the two-minute grace, a modern transient call surviving response closure, or a legacy request-key count remaining after active calls reach zero is a lifecycle defect.
67
67
 
68
68
  A Worker-requested `daemon_transport_error` or `daemon_liveness_timeout` is a retryable connection invalidation. The local daemon must close only the affected socket, preserve pending-call detach semantics, and reconnect; it must not enter the fatal `relay_protocol_error` path or exit for launchd to restart. The Worker uses close code 1012 for these transient cases. The daemon also recognizes the bounded close reasons `daemon pong failed`, `daemon send failed`, and `daemon liveness timeout` if the preceding error frame is not delivered. Unknown error codes, authentication rejection, and server identity/version mismatch remain permanent failures and require operator action.
69
69
 
70
70
  For modern MCP `2026-07-28`, every POST advertises both `application/json` and `text/event-stream` with valid positive HTTP quality values, carries protocol version and capabilities in request `_meta`, and mirrors the version/method/applicable name into validated HTTP headers. The actual `/mcp` Origin must be absent, same-origin, built-in, or explicitly allowlisted; CORS preflight accepts only fixed protocol headers plus exact catalog-declared `Mcp-Param-*` names. A JSON response completes immediately; a streamed `tools/call` receives a request-scoped SSE stream without event IDs. Closing that response stream is cancellation: the outer Worker observes request abort, response-body cancellation, or failed bounded keepalive delivery and sends one random internal capability without Authorization or DPoP headers. The Durable Object consumes it before OAuth only to remove the already-active matching pending call and send `cancel_call` when work has been dispatched; caller-supplied internal headers are stripped. Modern streams are never resumed through GET or `Last-Event-ID`. Local Wrangler does not propagate a raw TCP close into the Worker cancellation callbacks reliably, so deterministic proxy tests enforce this control path and live candidate verification must exercise it on the deployed edge.
71
71
 
72
- Legacy MCP `2025-11-25` retains the older delivery contract for existing hosts. Name, account-visible tool membership, and raw arguments are validated before any resumable record is allocated; malformed or role-hidden calls return `-32602` with no daemon dispatch. For a valid call, the outer Worker emits sequence-zero and sequence-one event IDs while `BridgeRoom` persists bounded session-bound stream/call ownership before daemon dispatch. A compatible legacy host may recover with authenticated `GET /mcp`, its original `Mcp-Session-Id`, and `Last-Event-ID`; it must not repeat the POST. Legacy records are token/session-bound, retained for at most two minutes, limited to 64 streams, and persist at most 1.5 MiB of terminal JSON. Errors `-32002`, `-32003`, and `-32005` in this area are legacy recovery diagnostics, not modern protocol errors. Caller-supplied internal stream headers are removed at the public boundary in both eras.
72
+ Legacy MCP `2025-11-25` retains the older delivery contract for existing hosts. Name, account-visible tool membership, and raw arguments are validated before any resumable record is allocated; malformed or role-hidden calls return `-32602` with no daemon dispatch. For a valid call, the outer Worker emits sequence-zero and sequence-one event IDs while `BridgeRoom` persists bounded session-bound stream/call ownership before daemon dispatch. A compatible legacy host should recover with authenticated `GET /mcp`, its original `Mcp-Session-Id`, and `Last-Event-ID`. If transport loss makes the original POST preparation or terminal response uncertain, an exact signed-session retry is safe throughout the stream's bounded recovery lifetime: the request identity and canonical argument fingerprint reattach it to the active or terminal stream, while changed arguments are rejected. Intentional new work must use a fresh typed request ID until that record expires or the client explicitly acknowledges sequence one, which deletes the replay record. Sessionless legacy POSTs remain independent for compatibility with clients that share one bearer token; without a signed session they do not receive POST idempotency, the outer Worker does not retry an ambiguous prepare, and the client must not blindly repeat an ambiguous side-effecting request. Legacy records are token/session-bound, retained for at most two minutes, limited to 64 streams, and persist at most 1.5 MiB of terminal JSON. Errors `-32002`, `-32003`, and `-32005` in this area are legacy recovery diagnostics, not modern protocol errors. Caller-supplied internal stream headers are removed at the public boundary in both eras.
73
73
 
74
74
  The daemon-to-Worker terminal protocol is at-least-once until `tool_result_ack`. Queueing a WebSocket frame is not durable delivery: the runtime retains a bounded terminal envelope, replays it after same-daemon reconnect or heartbeat, and removes it only after acknowledgement or the authoritative `resume_calls` reconciliation excludes it. The modern public stream has no replay surface; the legacy terminal store is generation-checked and exactly-once from the client's recovery perspective. `server_info.worker.observability.calls.unmatched_results` counts late results whose pending owner was already removed. A small increase may accompany cancellation or timeout races; sustained growth together with old pending calls indicates mixed versions or a lifecycle defect. The counter contains no arguments or result data.
75
75
 
@@ -85,7 +85,7 @@ A reconnect warning proves a transport interruption, not a daemon crash. Compare
85
85
 
86
86
  Brief retryable outages reconnect automatically. A persistent outage emits bounded summaries; identity/version mismatch, authentication rejection, and unexpected protocol messages remain permanent failures requiring version convergence or credential repair. Compare outage intervals with sleep/wake records and `diagnose_runtime.runtime.relay.heartbeat` before classifying them as active network faults; local stdio `server_info.runtime.relay.heartbeat` exposes the same state. A nonzero `event_loop_stall_count` with a large `max_event_loop_lag_ms` means the local daemon was not scheduled promptly; during recovery grace it sends a new heartbeat and deliberately postpones disconnect. That is distinct from a relay that remains silent after local scheduling has recovered. Use `--verbose` only when close codes, heartbeat deadlines, and retry delays are required.
87
87
 
88
- A foreground MCP tool is not a durable job. Hosted clients can impose a request ceiling below the local/stdio schema, so the remote catalog accepts at most 85 seconds and reserves terminal-delivery margin. Longer work belongs in `start_process` plus bounded `read_process`, or in a managed job. Keep mutation and verification in independently terminal calls when a host exposes only a foreground shell tool.
88
+ A foreground MCP tool is not a durable job. Every advertised MCP surface accepts at most 85 seconds and reserves terminal-delivery margin. Relay execution applies the same 30- or 60-second default when the field is omitted, and a registered-command manifest cannot extend a relay call past 85 seconds. An owner-local registered command may retain a longer explicit manifest timeout because it does not depend on a hosted response stream. Longer remote work belongs in `start_process` plus bounded `read_process`, or in a managed job. Keep mutation and verification in independently terminal calls when a host exposes only a foreground shell tool.
89
89
 
90
90
  The daemon honors `HTTPS_PROXY`/`HTTP_PROXY` and `NO_PROXY` through standard environment-proxy resolution for remote Worker health and relay traffic. `wss:` targets use HTTPS proxy selection and `ws:` targets use HTTP proxy selection. Only HTTP and HTTPS proxy URLs are accepted. Invalid URLs or unsupported protocols fail startup with corrective guidance instead of entering the reconnect loop. `diagnose_runtime.runtime.relay.network_route` reports remotely, while local stdio `server_info.runtime.relay.network_route` reports `system-network-stack`, `application-http-proxy`, or `invalid-application-proxy-configuration`. This field describes only Machine Bridge application-level proxy selection: an operating-system VPN/TUN may still intercept `system-network-stack` traffic. `network_route_scope`, outage timestamps/durations, close category/code, transport error class, and next retry timing make that distinction explicit; proxy endpoints and credentials are never returned or logged. The browser-broker CLI health probe is a separate loopback-only path: it accepts only canonical `127.0.0.1`, uses direct Node HTTP with no proxy agent, and does not depend on `NO_PROXY`.
91
91
 
@@ -199,7 +199,7 @@ Uninstall acquires a state-root `maintenance.lock` that blocks new profile/state
199
199
 
200
200
  ### Lifecycle and pending-call diagnosis
201
201
 
202
- Remote `diagnose_runtime.runtime.lifecycle` reports `ready`, `starting`, `running`, `failed`, `stopping`, or `stopped`; `diagnose_runtime.observability.in_flight_calls` reports ordinary versus reserved local capacity; and `diagnose_runtime.runtime.processes` distinguishes active calls, draining calls whose protocol result already settled, currently terminating processes, and pending escalation checks. It also returns `runtime.execution_guardrails` and `runtime.security_audit`. Local stdio `server_info` exposes the equivalent fields under `server_info.runtime`, `server_info.observability`, and `server_info.security_audit`. A returned timeout therefore does not claim that all kernel or descendant work has already stopped. Worker `server_info.worker.pending_calls` reports the internal-call index, legacy request-key index, detached-call count, ordinary/control capacity, and current ordinary/control occupancy across transient and durable calls. Modern HTTP stream closure should remove its transient stream owner and pending daemon call; there is no modern replay record or request-key entry. Legacy terminal result, explicit cancellation, timeout, or reconnect-grace expiry must return active/detached/request-key counts to zero. During a brief daemon interruption, legacy `active` and `request_keys` may remain nonzero while `detached` identifies the recoverable subset; after same-instance readiness, `detached` returns to zero without losing those requests. Nonzero legacy request-key counts after active calls reach zero indicate a lifecycle defect rather than normal load. `worker.observability.calls.unmatched_results` is the bounded counter for late results that no longer have a receiver.
202
+ Remote `diagnose_runtime.runtime.lifecycle` reports `ready`, `starting`, `running`, `failed`, `stopping`, or `stopped`; `diagnose_runtime.observability.in_flight_calls` reports ordinary versus reserved local capacity; and `diagnose_runtime.runtime.processes` distinguishes active calls, draining calls whose protocol result already settled, currently terminating processes, and pending escalation checks. It also returns `runtime.execution_guardrails` and `runtime.security_audit`. Local stdio `server_info` exposes the equivalent fields under `server_info.runtime`, `server_info.observability`, and `server_info.security_audit`. A returned timeout therefore does not claim that all kernel or descendant work has already stopped. Worker `server_info.worker.pending_calls` reports the internal-call index, legacy request-key index, detached-call count, ordinary/control capacity, and current ordinary/control occupancy across transient and durable calls. Modern HTTP stream closure should remove its transient stream owner and pending daemon call; there is no modern replay record or request-key entry. Legacy terminal result, explicit cancellation, timeout, or reconnect-grace expiry must return active/detached/pending-call request-key counts to zero; the stream-level idempotency identity remains only for bounded replay retention. During a brief daemon interruption, legacy `active` and `request_keys` may remain nonzero while `detached` identifies the recoverable subset; after same-instance readiness, `detached` returns to zero without losing those requests. Nonzero legacy request-key counts after active calls reach zero indicate a lifecycle defect rather than normal load. `worker.observability.calls.unmatched_results` is the bounded counter for late results that no longer have a receiver.
203
203
 
204
204
  Stable errors include `policy_denied`, `invalid_request`, `timeout`, `cancelled`, `network_error`, `unavailable`, `limit_exceeded`, and `integrity_error`, with retryability metadata. Diagnose by code first; free-form messages are guidance, not an API contract.
205
205
 
@@ -306,7 +306,7 @@ Defense-in-depth limits include:
306
306
  - process stdin write: 64 KiB per call;
307
307
  - local simultaneous tool calls: 16 total, with 14 ordinary slots and two reserved for bounded control-plane diagnosis/recovery;
308
308
  - Worker pending daemon calls: 32 total, with 30 ordinary slots and two reserved for bounded control-plane diagnosis/recovery;
309
- - local/stdio command timeout schema: 1–600 seconds; the remote Worker schema is 1–85 seconds with tool-specific 30- or 60-second defaults, reserves five seconds for terminal delivery, and rejects larger values before dispatch;
309
+ - synchronous foreground timeout schema on every MCP surface: 1–85 seconds with tool-specific 30- or 60-second defaults; the Worker reserves five seconds for terminal delivery, and the relay execution boundary reapplies the same default/ceiling before process spawn, including registered commands whose local manifest is longer; use process sessions or managed jobs for longer remote work;
310
310
  - process-session read wait: at most 30 seconds, measured with monotonic elapsed time;
311
311
  - direct directory result: 10,000 entries and 4 MiB of path metadata;
312
312
  - recursive walk: 200,000 visited entries;
package/docs/TESTING.md CHANGED
@@ -82,7 +82,7 @@ The suite includes:
82
82
  - shared no-follow bounded-file reads for normal files, over-limit data, directories, and symbolic links;
83
83
  - owner-only directory enforcement rejecting final symlinks, failing closed on POSIX chmod errors, verifying `0700`, and retaining Windows portability; Worker temporary-secret lifecycle coverage for process-start-bound names, valid stale-owner reclamation, ambiguous-owner retention, `0600` mode, deletion failures, and simultaneous deployment/cleanup failures;
84
84
  - SARIF security-gate behavior for unknown findings, exact accepted rule/path matches, path mismatch rejection, rationale quality, and exception expiry;
85
- - deterministic property tests over hostile browser-protocol byte strings, canonical/custom policy combinations, argv bounds/NULs, and a real direct process proving shell metacharacters remain literal argv; process-tree tests also assert Darwin uses a target-PGID `ps` query, preserve the global inspection budget, and repeatedly prove anti-`SIGTERM` descendants exit after foreground timeout;
85
+ - deterministic property tests over hostile browser-protocol byte strings, canonical/custom policy combinations, argv bounds/NULs, and a real direct process proving shell metacharacters remain literal argv; direct timeout-alignment tests prove relay shell/direct-process defaults are 60 seconds, relay registered commands are capped at 85 seconds even when their owner manifest is longer, and owner-local registered commands retain the manifest budget; process-tree tests also assert Darwin uses a target-PGID `ps` query, preserve the global inspection budget, and repeatedly prove anti-`SIGTERM` descendants exit after foreground timeout;
86
86
  - prototype-shaped command, action, role, profile, form-field, keyboard, and resource names proving that inherited object properties are never interpreted as dispatch or authority; current-schema malformed OAuth roles are repaired to disabled reviewer accounts with credential revocation;
87
87
  - canonical Worker deployment URL extraction proving unrelated `/mcp`, `/healthz`, path-bearing, and wrong-name URLs cannot be persisted as upload evidence;
88
88
  - byte-exact UTF-8 DOM-source truncation across emoji and Chinese partial-code-point boundaries, including equality between the reported byte count and the encoded returned source;
@@ -93,7 +93,7 @@ The suite includes:
93
93
  - P-256 root generation, root-certified ephemeral session issuance, macOS trust-broker build/signature checks, signed WebSocket preflight, one-time transactional nonce consumption, challenge transcript binding, wrong-root/session/tamper/expiry/replay rejection, and prevention of unauthenticated candidate churn;
94
94
  - request-scoped effective authority and catalog-wide risk review; non-escalatable reviewer/editor/operator ceilings; authenticated-owner direct execution; control-plane root denial; external and sensitive path composition; persistence-target rejection; symbolic-link ancestor and patch-move canonicalization; owner-only browser/application/data-export and persistent-plan effects; account/client/refresh-family ownership of processes, output sessions, and jobs; delegated sandbox fail-closed behavior; legacy-lease non-consumption; and malformed-record rejection;
95
95
  - root-certified ephemeral P-256 account-administration requests with origin/method/path/body/key/time/nonce binding, transactional one-time nonce consumption, removal of the long-lived administration secret, certificate/signature/body tamper rejection, nonce replay rejection, malformed nonce-state fail-closed behavior, one-megabyte response bounds, immediate oversized-response cancellation, and strict successful JSON-object validation;
96
- - live local Worker OAuth registration and authorization metadata; PKCE, DCR, refresh rotation/replay, account/client/family revocation, DPoP, actual `/mcp` Origin checks, bounded CORS/CSP, exact callback handling, and bounded OAuth persistence; modern MCP `2026-07-28` per-request `_meta`, open-JSON structural budgets, bounded resource subscriptions, strict dual-media `Accept` quality values, mirrored header/body validation, `server/discover`, result identity, cache hints, same-token/same-request-ID concurrency, role-hidden/unknown/schema `-32602` non-dispatch, credential-free private cancellation (including public-header forgery and DPoP replay controls), request-scoped streaming, filtered `subscriptions/listen`, removed-method 404 behavior, and no session/replay leakage; plus the complete legacy MCP `2025-11-25` initialize, pre-persistence raw-argument validation, signed-session cancellation, sequence event, recovery GET, `Last-Event-ID`, duplicate-domain, and replay-isolation suite. The same integration covers shared Worker/local timeout ceilings, daemon candidate/probing/ready replacement, malformed daemon messages, rich content, account-role projection, and stable catalog behavior before/during/after daemon availability.
96
+ - live local Worker OAuth registration and authorization metadata; PKCE, DCR, refresh rotation/replay, account/client/family revocation, DPoP, actual `/mcp` Origin checks, bounded CORS/CSP, exact callback handling, and bounded OAuth persistence; modern MCP `2026-07-28` per-request `_meta`, open-JSON structural budgets, bounded resource subscriptions, strict dual-media `Accept` quality values, mirrored header/body validation, `server/discover`, result identity, cache hints, same-token/same-request-ID concurrency, role-hidden/unknown/schema `-32602` non-dispatch, credential-free private cancellation (including public-header forgery and DPoP replay controls), request-scoped streaming, filtered `subscriptions/listen`, removed-method 404 behavior, and no session/replay leakage; plus the complete legacy MCP `2025-11-25` initialize, pre-persistence raw-argument validation, signed-session cancellation, sequence event, recovery GET, `Last-Event-ID`, bounded idempotent-retry/conflict domain, and replay-isolation suite. The same integration covers the shared 85-second foreground timeout ceiling and matching local relay execution defaults, signed-session cloned prepare retry, DPoP proof/retry-ID atomic binding and replay isolation, sessionless no-retry safety, and active-or-terminal stream reattachment, canonical argument fingerprints, concurrent legacy subscriber multicast/limits/cancellation cleanup, layered global/subject rate-limit identity, daemon candidate/probing/ready replacement, malformed daemon messages, rich content, account-role projection, and stable catalog behavior before/during/after daemon availability.
97
97
  - local runtime proof that one blocked tool handler does not serialize an independent handler, plus relay fault injection proving an undeliverable terminal result interrupts the ambiguous socket and enters reconnect backoff.
98
98
  - a real headless-Chrome OAuth navigation regression with bounded browser startup, DevTools discovery, WebSocket connection, and per-command deadlines, covering four cases: `form-action 'self'` blocks the first cross-origin callback, allowing only the registered callback blocks the regional redirect, allowing the registered and regional callbacks blocks the final Copilot Studio redirect, and the complete policy preserves `code` and `state` through all three cross-origin hops. Linux CI fails if Chrome is unavailable; other environments skip only this browser executable check while retaining the Worker CSP assertions.
99
99
 
@@ -112,7 +112,7 @@ It opens Calculator, activates it through the fixed JXA helper, verifies structu
112
112
  For deterministic release validation, perform an isolated-profile smoke test with the packaged unpacked extension; a Playwright persistent Chromium context is acceptable only as that isolated harness. When the requirement is specifically to prove control of the user's ordinary browser, the user must load the same unpacked directory into that known daily Chromium profile; status can verify the extension version/protocol and that Machine Bridge did not launch a browser, but cannot infer profile identity. Then use a localhost no-store fixture in a newly created tab. Do not enumerate, read, or mutate unrelated existing tabs. In both modes, inspect and reuse refs, exercise waits/forms/trusted input/open Shadow DOM/screenshots, verify final live DOM, and close the fixture tab.
113
113
 
114
114
 
115
- `npm run control-plane-resilience:test` is the focused accident-regression gate. It exercises synchronous/asynchronous audit failures, persistent-state corruption and retention anchoring, POSIX/Windows process-tree fallbacks, escalation-supervisor exception isolation, mixed transient/durable Worker capacity, and the shared 30+2 / 14+2 control-plane admission contract. It is part of the fast plan rather than coverage-only evidence.
115
+ `npm run control-plane-resilience:test` is the focused accident-regression gate. It exercises synchronous/asynchronous audit failures, cached-state invalidation after external alteration, count- and byte-bounded retention anchoring, POSIX/Windows process-tree fallbacks, escalation-supervisor exception isolation, mixed transient/durable Worker capacity, and the shared 30+2 / 14+2 control-plane admission contract. `npm run security-audit:test` additionally runs two independent audit workers against one owner-only state file and requires continuous sequence numbers with no lost events, covering the lock release/acquire race. Both are part of the fast plan rather than coverage-only evidence.
116
116
 
117
117
  - control-plane resilience under host pressure: local event-loop stalls versus genuine relay silence, fresh-heartbeat recovery grace, asynchronous process-group identity capture before `SIGTERM`, bounded post-signal revalidation, draining-process visibility after result settlement, two reserved diagnostic slots at both Worker and local layers under mixed transient/durable ordinary-call saturation, non-blocking audit dispatch, batched Worker persistence, queue/drop health, warning suppression, and privacy-safe audit projection;
118
118
  - relay outage diagnostics and recovery: application-proxy versus OS-network scope, timestamped close/outage/recovery fields, fifteen-second maximum reconnect delay, heartbeat timeout, same-instance call continuation, Worker pong/welcome send failure, and `diagnose_runtime` relay history;
@@ -171,7 +171,7 @@ Use a narrower profile, separate OS account, container, or VM when prompts, repo
171
171
 
172
172
  ### Public Worker endpoint and quota guards
173
173
 
174
- The default public endpoint remains the automatically provisioned `workers.dev` origin so ordinary users need no DNS zone or custom domain. The in-Worker Rate Limiting binding is deliberately a Durable Object burst guard, not an authentication boundary or an exact global quota accountant. It runs after a Worker invocation begins, is scoped by Cloudflare location, and currently uses one deployment-wide stateful bucket per location. A concentrated source can therefore cause a temporary localized denial for other clients sharing that location, while distributed traffic can still consume the account-wide Workers request allowance. Binding failure is fail-open to avoid turning a quota helper into an account outage. Operators who already control an external edge may add pre-Worker filtering independently, but the public package does not require or assume a private domain.
174
+ The default public endpoint remains the automatically provisioned `workers.dev` origin so ordinary users need no DNS zone or custom domain. The in-Worker Rate Limiting bindings are deliberately Durable Object burst guards, not authentication boundaries or exact global quota accountants. They run after a Worker invocation begins and are scoped by Cloudflare location. A high-capacity route/Worker bucket bounds aggregate stateful pressure, while a lower route/subject bucket isolates a presented credential or an anonymous network identity through an internal truncated SHA-256 key. Raw credentials and addresses are not logged or returned by diagnostics; the network-address hash is a bucketing mechanism, not cryptographic anonymization of low-entropy IP space. Random invalid credentials cannot bypass the aggregate bucket. Distributed traffic can still consume the account-wide Workers request allowance, and a sufficiently concentrated source can exhaust its own local subject bucket. Binding failure is fail-open to avoid turning a quota helper into an account outage. Operators who already control an external edge may add pre-Worker filtering independently, but the public package does not require or assume a private domain.
175
175
 
176
176
  ### Bearer clients
177
177
 
@@ -217,7 +217,7 @@ Code cannot create a second independent reviewer, npm OIDC trust relationship, p
217
217
 
218
218
  Regression suites cover:
219
219
 
220
- - account roles, trusted clients, account-version revocation, refresh-family rotation/replay, DPoP proofs, and non-escalatable effective authority;
220
+ - account roles, trusted clients, account-version revocation, refresh-family rotation/replay, DPoP proofs, and non-escalatable effective authority; Transparent legacy prepare retry does not weaken that replay boundary: a trusted outer Worker creates an unguessable internal retry ID, public copies of the internal header are stripped, and the Durable Object accepts the consumed proof again only for the same atomically stored ID, a live primary nonce, and a maximum of four uses. The identifier is scoped to one public request and is not returned to the client.
221
221
  - root-certified ephemeral sessions, preflight nonce replay, daemon challenge binding, readiness, reconnect, and candidate replacement;
222
222
  - signed account administration, client revocation, bounded strict-JSON administration responses, and removal of the long-lived administration secret;
223
223
  - control-plane path denial, path canonicalization, symlink handling, sensitive/persistence targets, and object ownership;
@@ -232,7 +232,7 @@ Discover installed local applications and launchers without reading their docume
232
232
 
233
233
  **Open local application**
234
234
 
235
- Open a named local application, optionally with a document or URL target, using the operating system launcher.
235
+ Open a named local application, optionally with a document or URL target, using the operating system launcher. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
236
236
 
237
237
  | Contract field | Value |
238
238
  |---|---|
@@ -265,7 +265,7 @@ Open a named local application, optionally with a document or URL target, using
265
265
  "timeout_seconds": {
266
266
  "type": "integer",
267
267
  "minimum": 1,
268
- "maximum": 120,
268
+ "maximum": 85,
269
269
  "default": 30
270
270
  }
271
271
  },
@@ -279,7 +279,7 @@ Open a named local application, optionally with a document or URL target, using
279
279
 
280
280
  **Inspect local application UI**
281
281
 
282
- Inspect a bounded macOS Accessibility tree for a running application. Requires local Accessibility permission and never executes arbitrary AppleScript supplied by the caller.
282
+ Inspect a bounded macOS Accessibility tree for a running application. Requires local Accessibility permission and never executes arbitrary AppleScript supplied by the caller. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
283
283
 
284
284
  | Contract field | Value |
285
285
  |---|---|
@@ -325,7 +325,7 @@ Inspect a bounded macOS Accessibility tree for a running application. Requires l
325
325
  "timeout_seconds": {
326
326
  "type": "integer",
327
327
  "minimum": 1,
328
- "maximum": 120,
328
+ "maximum": 85,
329
329
  "default": 30
330
330
  }
331
331
  },
@@ -339,7 +339,7 @@ Inspect a bounded macOS Accessibility tree for a running application. Requires l
339
339
 
340
340
  **Operate local application UI**
341
341
 
342
- Perform a structured macOS Accessibility action on a matched UI element. Text can be supplied through a registered local resource so secret values do not enter MCP arguments.
342
+ Perform a structured macOS Accessibility action on a matched UI element. Text can be supplied through a registered local resource so secret values do not enter MCP arguments. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
343
343
 
344
344
  | Contract field | Value |
345
345
  |---|---|
@@ -429,7 +429,7 @@ Perform a structured macOS Accessibility action on a matched UI element. Text ca
429
429
  "timeout_seconds": {
430
430
  "type": "integer",
431
431
  "minimum": 1,
432
- "maximum": 120,
432
+ "maximum": 85,
433
433
  "default": 30
434
434
  }
435
435
  },
@@ -496,7 +496,7 @@ Open the local pairing page and return the packaged unpacked-extension path for
496
496
 
497
497
  **List browser tabs**
498
498
 
499
- Read the tab inventory from the paired existing Chromium profile without creating, activating, or closing a tab. Use browser_manage_tabs for tab mutations.
499
+ Read the tab inventory from the paired existing Chromium profile without creating, activating, or closing a tab. Use browser_manage_tabs for tab mutations. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
500
500
 
501
501
  | Contract field | Value |
502
502
  |---|---|
@@ -524,7 +524,7 @@ Read the tab inventory from the paired existing Chromium profile without creatin
524
524
  "timeout_seconds": {
525
525
  "type": "integer",
526
526
  "minimum": 1,
527
- "maximum": 120,
527
+ "maximum": 85,
528
528
  "default": 30
529
529
  }
530
530
  }
@@ -535,7 +535,7 @@ Read the tab inventory from the paired existing Chromium profile without creatin
535
535
 
536
536
  **Manage browser tabs**
537
537
 
538
- Create, activate, or close tabs in the paired existing Chromium profile. Use browser_list_tabs when only a read-only tab inventory is needed.
538
+ Create, activate, or close tabs in the paired existing Chromium profile. Use browser_list_tabs when only a read-only tab inventory is needed. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
539
539
 
540
540
  | Contract field | Value |
541
541
  |---|---|
@@ -577,7 +577,7 @@ Create, activate, or close tabs in the paired existing Chromium profile. Use bro
577
577
  "timeout_seconds": {
578
578
  "type": "integer",
579
579
  "minimum": 1,
580
- "maximum": 120,
580
+ "maximum": 85,
581
581
  "default": 30
582
582
  }
583
583
  },
@@ -591,7 +591,7 @@ Create, activate, or close tabs in the paired existing Chromium profile. Use bro
591
591
 
592
592
  **Read browser page source**
593
593
 
594
- Read bounded raw serialized DOM HTML from the active or selected tab when source markup is required. Use browser_inspect_page for semantic elements, actionability, reusable refs, and structured interaction planning. max_bytes is one aggregate budget across at most 64 accessible frames.
594
+ Read bounded raw serialized DOM HTML from the active or selected tab when source markup is required. Use browser_inspect_page for semantic elements, actionability, reusable refs, and structured interaction planning. max_bytes is one aggregate budget across at most 64 accessible frames. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
595
595
 
596
596
  | Contract field | Value |
597
597
  |---|---|
@@ -621,7 +621,7 @@ Read bounded raw serialized DOM HTML from the active or selected tab when source
621
621
  "timeout_seconds": {
622
622
  "type": "integer",
623
623
  "minimum": 1,
624
- "maximum": 120,
624
+ "maximum": 85,
625
625
  "default": 30
626
626
  },
627
627
  "frame_id": {
@@ -640,7 +640,7 @@ Read bounded raw serialized DOM HTML from the active or selected tab when source
640
640
 
641
641
  **Inspect browser page**
642
642
 
643
- Inspect a bounded semantic/actionability snapshot with reusable element refs for structured browser decisions and actions. This is not raw page source; use browser_get_source when serialized DOM HTML is required. The aggregate element budget spans at most 64 accessible frames.
643
+ Inspect a bounded semantic/actionability snapshot with reusable element refs for structured browser decisions and actions. This is not raw page source; use browser_get_source when serialized DOM HTML is required. The aggregate element budget spans at most 64 accessible frames. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
644
644
 
645
645
  | Contract field | Value |
646
646
  |---|---|
@@ -674,7 +674,7 @@ Inspect a bounded semantic/actionability snapshot with reusable element refs for
674
674
  "timeout_seconds": {
675
675
  "type": "integer",
676
676
  "minimum": 1,
677
- "maximum": 120,
677
+ "maximum": 85,
678
678
  "default": 30
679
679
  },
680
680
  "frame_id": {
@@ -693,7 +693,7 @@ Inspect a bounded semantic/actionability snapshot with reusable element refs for
693
693
 
694
694
  **Wait for browser state**
695
695
 
696
- Wait until all supplied URL, load, text, and element-state conditions are satisfied in an existing browser tab.
696
+ Wait until all supplied URL, load, text, and element-state conditions are satisfied in an existing browser tab. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
697
697
 
698
698
  | Contract field | Value |
699
699
  |---|---|
@@ -794,7 +794,7 @@ Wait until all supplied URL, load, text, and element-state conditions are satisf
794
794
  "timeout_seconds": {
795
795
  "type": "integer",
796
796
  "minimum": 1,
797
- "maximum": 120,
797
+ "maximum": 85,
798
798
  "default": 30
799
799
  }
800
800
  }
@@ -805,7 +805,7 @@ Wait until all supplied URL, load, text, and element-state conditions are satisf
805
805
 
806
806
  **Operate browser page**
807
807
 
808
- Perform one structured navigation or page action in the user's existing browser tab without arbitrary JavaScript. Automatic trusted-input fallback occurs only before any DevTools Input command starts; ambiguous post-dispatch failures require inspection before retry.
808
+ Perform one structured navigation or page action in the user's existing browser tab without arbitrary JavaScript. Automatic trusted-input fallback occurs only before any DevTools Input command starts; ambiguous post-dispatch failures require inspection before retry. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
809
809
 
810
810
  | Contract field | Value |
811
811
  |---|---|
@@ -919,7 +919,7 @@ Perform one structured navigation or page action in the user's existing browser
919
919
  "timeout_seconds": {
920
920
  "type": "integer",
921
921
  "minimum": 1,
922
- "maximum": 120,
922
+ "maximum": 85,
923
923
  "default": 30
924
924
  },
925
925
  "frame_id": {
@@ -953,7 +953,7 @@ Perform one structured navigation or page action in the user's existing browser
953
953
 
954
954
  **Fill complex browser form**
955
955
 
956
- Fill multiple fields and optionally submit a complex form. Sensitive values can come from registered local resources; errors identify possible earlier mutations without returning field values.
956
+ Fill multiple fields and optionally submit a complex form. Sensitive values can come from registered local resources; errors identify possible earlier mutations without returning field values. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
957
957
 
958
958
  | Contract field | Value |
959
959
  |---|---|
@@ -1115,7 +1115,7 @@ Fill multiple fields and optionally submit a complex form. Sensitive values can
1115
1115
  "timeout_seconds": {
1116
1116
  "type": "integer",
1117
1117
  "minimum": 1,
1118
- "maximum": 180,
1118
+ "maximum": 85,
1119
1119
  "default": 60
1120
1120
  },
1121
1121
  "frame_id": {
@@ -1139,7 +1139,7 @@ Fill multiple fields and optionally submit a complex form. Sensitive values can
1139
1139
 
1140
1140
  **Capture browser screenshot**
1141
1141
 
1142
- Capture the visible area of the active or selected tab from the paired existing browser profile, restore the previous active tab when safe, avoid focusing another window, and return native MCP image content.
1142
+ Capture the visible area of the active or selected tab from the paired existing browser profile, restore the previous active tab when safe, avoid focusing another window, and return native MCP image content. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
1143
1143
 
1144
1144
  | Contract field | Value |
1145
1145
  |---|---|
@@ -1177,7 +1177,7 @@ Capture the visible area of the active or selected tab from the paired existing
1177
1177
  "timeout_seconds": {
1178
1178
  "type": "integer",
1179
1179
  "minimum": 1,
1180
- "maximum": 120,
1180
+ "maximum": 85,
1181
1181
  "default": 30
1182
1182
  }
1183
1183
  }
@@ -1188,7 +1188,7 @@ Capture the visible area of the active or selected tab from the paired existing
1188
1188
 
1189
1189
  **Upload browser files**
1190
1190
 
1191
- Populate a browser file input from registered local resource files without returning file contents through MCP results.
1191
+ Populate a browser file input from registered local resource files without returning file contents through MCP results. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
1192
1192
 
1193
1193
  | Contract field | Value |
1194
1194
  |---|---|
@@ -1280,7 +1280,7 @@ Populate a browser file input from registered local resource files without retur
1280
1280
  "timeout_seconds": {
1281
1281
  "type": "integer",
1282
1282
  "minimum": 1,
1283
- "maximum": 180,
1283
+ "maximum": 85,
1284
1284
  "default": 60
1285
1285
  },
1286
1286
  "frame_id": {
@@ -1461,7 +1461,7 @@ List effective direct-argv commands from project manifests and safe automatic pa
1461
1461
 
1462
1462
  **Run registered local command**
1463
1463
 
1464
- Prefer this when the repository already defines the desired operation as a registered command or package script. It runs the fixed argv/cwd/timeout contract without shell reinterpretation; use exec_command for ad hoc pipelines or run_process for an unregistered executable argv. Large output is retained for read_process.
1464
+ Prefer this when the repository already defines the desired operation as a registered command or package script. It runs the fixed argv/cwd/timeout contract without shell reinterpretation; use exec_command for ad hoc pipelines or run_process for an unregistered executable argv. Large output is retained for read_process. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
1465
1465
 
1466
1466
  | Contract field | Value |
1467
1467
  |---|---|
@@ -1499,7 +1499,8 @@ Prefer this when the repository already defines the desired operation as a regis
1499
1499
  "timeout_seconds": {
1500
1500
  "type": "integer",
1501
1501
  "minimum": 1,
1502
- "maximum": 600
1502
+ "maximum": 85,
1503
+ "default": 60
1503
1504
  }
1504
1505
  },
1505
1506
  "required": [
@@ -1994,7 +1995,7 @@ Return bounded metadata and patch output for one revision without running reposi
1994
1995
 
1995
1996
  **Run process directly**
1996
1997
 
1997
- Run an explicit executable plus argv when no shell syntax is needed and no registered command fits. This avoids quoting, globbing, pipelines, and redirection, but it is not a sandbox; use exec_command when Bash composition is the convenient choice. Large output is retained for read_process.
1998
+ Run an explicit executable plus argv when no shell syntax is needed and no registered command fits. This avoids quoting, globbing, pipelines, and redirection, but it is not a sandbox; use exec_command when Bash composition is the convenient choice. Large output is retained for read_process. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
1998
1999
 
1999
2000
  | Contract field | Value |
2000
2001
  |---|---|
@@ -2025,8 +2026,8 @@ Run an explicit executable plus argv when no shell syntax is needed and no regis
2025
2026
  "timeout_seconds": {
2026
2027
  "type": "integer",
2027
2028
  "minimum": 1,
2028
- "maximum": 600,
2029
- "default": 120
2029
+ "maximum": 85,
2030
+ "default": 60
2030
2031
  }
2031
2032
  },
2032
2033
  "required": [
@@ -2802,7 +2803,7 @@ Request cancellation of a detached managed job. The runner terminates the active
2802
2803
 
2803
2804
  **Execute shell command**
2804
2805
 
2805
- Run Bash-compatible shell composition in the workspace: pipelines, redirection, globbing, conditionals, or compact multi-command probes. This is the convenient general escape hatch, not a sandbox, and has the local user's operating-system authority. Prefer run_local_command for an existing fixed project command and run_process when no shell syntax is needed. Large output is retained for read_process.
2806
+ Run Bash-compatible shell composition in the workspace: pipelines, redirection, globbing, conditionals, or compact multi-command probes. This is the convenient general escape hatch, not a sandbox, and has the local user's operating-system authority. Prefer run_local_command for an existing fixed project command and run_process when no shell syntax is needed. Large output is retained for read_process. Foreground execution is limited to 85 seconds; use process sessions or managed jobs for longer work.
2806
2807
 
2807
2808
  | Contract field | Value |
2808
2809
  |---|---|
@@ -2826,8 +2827,8 @@ Run Bash-compatible shell composition in the workspace: pipelines, redirection,
2826
2827
  "timeout_seconds": {
2827
2828
  "type": "integer",
2828
2829
  "minimum": 1,
2829
- "maximum": 600,
2830
- "default": 120
2830
+ "maximum": 85,
2831
+ "default": 60
2831
2832
  }
2832
2833
  },
2833
2834
  "required": [