Correct the downlink-write activity contract for compressed frames: a write
that does not complete at once counts, and a frame above the threshold
deflates asynchronously so its completion always counts. The Host compresses
only its own outbound frames; uplink compression remains the peer's choice.
Stabilize the compressed-write liveness test with a 500 ms heartbeat interval
and assert the bounded half: after the downlink stops, missing Pongs still
terminate the socket.
Refs #6476
Restore the `ws` default `maxPayload`: the pre-change mux passed no limit, so
the 1 MiB ceiling was an unrelated breaking change. Remove the exported
constant and the oversize-frame test, and drop the matching docs.
Pin the per-frame compression threshold and the accepted compressed-write
liveness consequence with regression tests, correct the queued-write
documentation, move the Agent Note to `implemented/`, and sync the Remote
duplex stream note with `websocketCompression`.
Refs #6476
Add a `websocketCompression` Gateway config field (`'deflate'` by default,
`'none'` to disable) and negotiate `permessage-deflate` on `/api/remote.mux`.
Frames whose JSON text reaches 1024 characters are compressed in the mux send
path, because `ws` ignores its `threshold` option under the default context
takeover. `maxPayload` is bounded by `max(streamInboxBytes, 1 MiB)` so one
compressed frame cannot inflate past the fixed ceiling.
Fixes#6476
A write that completes at once only reached the local send buffer, so
counting it delayed dead-peer detection until that buffer filled. Only a
write that waited in the full buffer now resets the count, and the
connection owns all three activity signals.
A congested remote.mux link can hold the Pong behind data frames, so the
Host terminated sockets that were still moving data and the Client then
reconnected and resent. Any received frame or completed downlink write now
resets the missed-heartbeat count.
Use Gateway event stream availability before dispatch without changing Peer or manifest synchronization APIs. Keep the response deadline after disconnection and cover connected, offline, and disconnect-after-dispatch behavior.
Tests bring the changed sources to full per-file coverage, the READMEs and
docs describe the uplink, the invocation context, and the Peer registry in
both languages, and the generated catalogs are refreshed.
Every Typert Remote stream accepts client-to-Host items on the same
logical stream. A method declares the uplink item type in its return
type, RemoteStream<Out, In>, and reads the items through
this.ctx.invocation.uplink(); the invocation also carries the calling
Peer. Client generated methods return a handle that iterates the
downlink and exposes send/end/dispose for the uplink.
- api/gateway: $host caches its RemoteHostFacts record and mints a new
one only when home changes, so snapshot readers compare by reference;
identity pinned in the client spec.
- client/ui-tool, client/ui-workspace: the hooks channel exposes the
host facts record as hostInfo and components select the field they
need (useHostInfo(info => info.home)); row-component contracts are
unchanged.
- api/gateway: mark terminal Remote-stream escapes (carrier retry
exhaustion and pre-acceptance end classification) as
RemoteError('gateway/internal') at the two escape points; marked
failures pass through verbatim. The carrier class stays the
retry-internal signal for carrierFailed and the ended(true) retry
trigger. Regression coverage lands on the session and workspace
stream consumers.
- client/ui-tool, client/ui-workspace: read $host.home through a hooks
observable subscribed to connection/reset; the slot renderer memoizes
inject results per entry, so the previous plain-value injection froze
home at the first render.
- client/ui-settings-models: components no longer receive ctx; apply
binds the credential and settings Remote operations into callbacks,
and the settings/conflict code judgment stays in the apply world.
- classify a carrier throw under a caller-aborted signal as
gateway/cancelled instead of gateway/internal, matching the code the
Host produces when the abort wins the wire round-trip.
- read $host facts from the construction-time Connection handle,
matching $stream; the service cannot be replaced without restarting
this plugin, so the live re-lookup was dead complexity.
- state rebuiltFailure's actual contract in its comment: codes pass
through verbatim without runtime validation.
- correct the @module name in dsh-util-time.
Single RemoteError with a merge-extensible, domain-prefixed code map;
owners throw at the failure point; streams surface marked failures;
clients consume ctx.remote directly with isRemoteFailure as the only
discrimination point and construct no failure instances.