* feat(webserver): add resource routes and in-process readResource
A webserver route or the fallback seat may now answer through a resource,
a function from a Fetch Request to a Response, besides the existing
node:http handler. register accepts either route form and registerFallback
accepts a handler or { resource }; existing handler callers are unchanged.
HTTP GET and HEAD requests reach a resource; webServer.readResource(request,
peer) follows the same match order and reaches only resources, so handler
routes stay unreachable to in-process readers.
client-modules registers /plugins as the fetchBundle resource, and
frontend-static claims the fallback seat with a resource that renders the
index for a supplied Peer, or runs Connection's existing authorizeIndex for
an HTTP request and renders for the browser Peer.
* fix(webserver): flush resource headers and keep one drain listener per response
Resource responses send their status and headers before the first body
chunk, register one drain/close listener pair per response so the gzip
middleware's zlib stream does not accumulate listeners across backpressure
waits, and readResource drops the body of a HEAD read as HTTP does. HTTP
requests and readResource share one responder lookup.
* chore(scripts): prune the retired client-modules unknown-cast baseline entry
* feat(frontend-static): let browsers revalidate dist files except the index
Every dist file other than the rendered index now carries
cache-control: no-cache and a weak ETag from a SHA-1 of its bytes; a
request whose If-None-Match equals that ETag receives an empty 304. The
index keeps sending no cache headers. In-process reads through
webServer.readResource revalidate the same way.
* fix(webserver): stop reading Host for resource URLs and copy no held body
A resource request's URL is the request target resolved against the same
placeholder origin the route match uses, so a missing or unparsable Host no
longer turns a dist or /plugins request into 400. The webserver ends a
resource response itself and treats any earlier close as an abort, which
also covers the Web Worker tunnel marking an aborted response ended first.
client-modules bundle bodies and frontend-static file bodies are the held
bytes themselves instead of copies, and frontend-static no longer exports
its internal serveStatic helper.
* docs(webserver): document resource route types and the handler-or-resource fallback
Paste WebResource and WebResourceRoute beside WebRoute on the web-server
subsystem page and link the catalog signatures to it, replace the remaining
'fallback handler' wording now that the seat takes a handler or a resource,
and note why the drain listener removal is a no-op under gzip.
Re-serializing a document with a foreign integer beyond 2^53 no longer rounds it;
the reload warning is tested; the GITLAB_TOKEN test no longer depends on the host
environment; README and JSDoc describe the tolerated top-level keys.
- WebServer.port reads number | undefined instead of masking the unbound
listener with an assertion; tests that dial it narrow the bound port.
- LegacyPrompt stays module-private; the web-app type import is a
devDependency only, since the emitted declarations never reference it.
- Copy: --public-url help names runtime context again, the public
deployments guide, web-app JSDoc, Config.publicUrl consumers, the Agent
Note matching rule, and three Known Limitations in the package README.
Review follow-up: `const closed` beside the boolean `this.closed` reads as the
flag, so name the promise `closing` at every site; the test-side comment
repeated the production mechanism verbatim, so each test keeps only the
ordering assumption its fake encodes.
chokidar's close() drops every listener while a scheduled write-settle poll
can still run. That straggler stats a document the home teardown is
deleting, which Windows reports as EPERM, and an 'error' emission with no
listener is rethrown as an uncaught exception: the whole coverage
partition fails while every test file passes.
Re-attach the provider's error listener across the close so the straggler
stays inside the provider, and make it a no-op after disposal.
Delete the now-unused llm-retry history helper, drop the unreachable
credentials-local refresh rejection handler, and add direct tests for the
gateway SRC skip of unprovided Services and llm-retry projection dedupe.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.