connection binds the web transport: it injects httpServer + apiProxy and
registers toFetchHandler(ctx.apiProxy) under the /api prefix (the node:http
to fetch bridge moves in from the webserver, keeping the res-close disconnect
detection and drain/close backpressure waits). hmr owns dev reload: a
stat-poll watch per graph row driven by clientModuleHost.onGraphChanged,
rebuilt(id) on content change, and the /plugins/events SSE route (GET/HEAD
guarded); frame types are single-sourced in events.ts shared by both halves.
The node half is ClientModuleHostService (ctx.clientModuleHost): it composes
the __DSH_BOOT__ graph by scanning loader entries for dshClient packages,
serves /plugins/<id>/client.js, taps the index render, and exposes
rebuilt/onRebuilt/onGraphChanged. Scanning is incremental per package — no
full-rescan path exists: internal/plugin marks the fiber's entry name dirty,
a flush reconciles each name against live entries, package metadata
(including negative verdicts) caches forever, and re-hashing is reachable
only through rebuilt(id). The browser half moves wholesale to the standard
./client export (ClientModuleSystem, parseBootManifest with the dual-view
BootManifest, and the adoption plugin face that reads the
window.__DSH_MODULES__ slot and provides ctx.modules).
HttpServerService provides ctx.httpServer: register(route) -> disposer
(duplicate patterns throw), tapIndex transforms in registration order, and
the bound port; matching is exact > longest prefix > static dist fallback
(403/405/SPA semantics preserved). The server listens on activation, answers
per-request failures with 400 + a log line instead of exiting the process,
and knows no harness concepts — the boot graph, bundle routes, SSE channel,
and /api prefix all moved to their owning plugins.
createApiProxy moves from dsh-host-runtime into dsh-host-apiproxy (the
dependency direction already pointed this way); the package now
default-exports ApiProxyService (config {provider, model}, provides
ctx.apiProxy) while staying transport-agnostic — it registers no routes.
Runtime keeps bootHost/startHost for the headless path with its import
re-anchored, and drops the mountWebPlugins roster mounting helper.
Keep inherited child prompt markers bounded by the normal silence fallback. Stage web-plugin rescans atomically and retain missing watch state until a successful rebuild.
The fake terminal retains earlier autocomplete frames, so the directory follow-up assertion could pass before the scoped lookup completed. Wait for the quoted file mention to be applied before submitting.