The service moved its "is a roster composed" reads to the derived root set; the invariant companion still read `config.roots`. In the shape this change exists for — an app configures nothing and the roster is the harness home alone — that made the advisory warning fire while the fail-loud invariant stayed silent, so an agent could address a model against an empty global layer unchecked. Both now read one source: `roots` exposes the resolved set, and the invariant asks it. That decides the behavior deliberately rather than by omission — a composition that mounts the roster now fails an unjoined agent whether its roots were configured or derived, and `includeUserRoot: false` with no configured roots is how a deployment keeps its agents on the host plane. Both shapes are pinned; the derived-only case fails against the old predicate. Three pieces of prose went stale with the first commit: the web-app bundle comment still called the writable root an assembly fact patched in by AppCLIEntry (removed in the profile-plugin-bundles refactor — `composeProfile` owns it now, and only for the shipped root), and the shipped skill and its Agent Note still called both roots "configuration". The README gains the resolved-roster reader and the discoverable-but-undeletable preset a second writable root produces.
@deepseek-ai/dsh-web-app
English | 中文
The dsh browser-surface bundle. cordis.patch.yml rides over dsh-base: it sets the coding persona, inserts the Web host rows (webserver, API gateway, workspace, projection cache, storage) and the browser plugin roster, the always-on client-plugin reload chain (dsh-client-hmr, idle until a rebuild watcher rewrites client bundles), and mounts this package's web-runtime glue plugin (config {printUrl, surfaceContext, trustedHosts}). That plugin resolves the built frontend dist through @deepseek-ai/dsh-frontend's exports, samples bind-dependent LAN trust once, provides it as webRuntime to the browser-trust fence and client roster, mounts the frontend-static fallback owner, registers the harness-source and web-surface prompt sections plus the bash-visible DSH_WEB_URL runtime variable when surfaceContext is true, and prints the dsh web: URL line when printUrl is true, after its Loader tree settles so a sibling failure cannot announce a dead app. This bundle also owns the app command line: the ordinary web-startup provider (src/startup.ts) injects ctx.cmdlineArgs (dsh-cmdline), parses --host, --port, repeatable --trusted-host, and the app's --help, then provides webStartup. Flag-configured rows inject that service and read it directly from lazy config, so nothing binds a port before argument resolution and dsh --profile web --help starts no server. dsh-headless is a sibling surface over the same base and does not mount this bundle.
Model Experience
Harness-source and Web-surface context
What the model sees
When surfaceContext is true, the harness:source section identifies the on-disk Harness implementation without claiming it is the working directory, and the app:web-surface global section (order −98) orients the model to the GUI: the canonical local URL, the "this page" referent, the update contract (the reload receiver is always on; no-refresh reloads additionally need the pnpm run dev:web watcher), and the instruction not to start replacement servers. DSH_WEB_URL additionally appears in the managed bash environment with its description, resolved per invocation from the live server. When it is false, neither section nor the variable is registered.
Token effect
One source line and one prompt paragraph per session plus two managed-environment variable lines; constant per process.
KV Cache effect
The prompt section sits near the system prompt's head and is stable for the life of the process (the port is a boot fact), so it does not invalidate the cache across turns.
Known Limitations and Deferred Work
- The frontend dist must be built —
require.resolveof the dist fails loud at activation with a build hint; there is no source-serving fallback. lanAddressesis a boot-time snapshot — interface changes after boot are not re-advertised; the printed LAN URL always matches the configured trust fence.