The inventory keeps dsh-app-boot an optional, type-only peer, so the walk
over an entry's owning groups moves behind ProfileRuntime.userDisables(): a
consumer with the runtime asks it, one without reports composition as before.
The module graph and the cordis catalog follow.
The `cordis.patch.yml` parser and the comment-preserving writer are the
Loader's format, not a mechanical primitive: they build on the include
plugin's dialect and the Loader's row types, which no other `util` package
does. They now live in `app-boot` as `src/patch-file.ts`, exported as
`@deepseek-ai/dsh-app-boot/patch-file` with its own bundle entry, so the
agent-preset roster and the plugin manager load them without the boot entry.
`dsh-patch-file` goes, with its README and catalog entries; `yaml` moves into
app-boot's dependencies.
A dozen plugin packages already depend on `dsh-app-boot`, so the roster
joining them costs nothing; the note records why the separate package was
tried and folded back.
Add the first-party open-in-app host and client packages with a localized Open In menu in the Web Session header.
Resolve installed applications on macOS, Windows, and Linux, launch workspace directories through platform-specific adapters, and remember the selected application.
Closes#1500.
Co-authored-by: ihsiang <[email protected]>
What plugin management does now lives in `dsh-plugin-manager`, a boot-group
package beside `app-boot`: `PluginInstaller` runs pnpm, probes, and judges new
packages over the profile on disk, and `PluginManager` adds every operation
over the booted tree, each refusal a `PluginOperationError` with a `plugins/*`
code and no Remote protocol attached. The manager reads the profile runtime,
the preset roster, and the running-agent count through per-call readers, and
the roster only as `PresetLayers`, so the package depends on neither the
roster nor the agent registry.
`dsh-host-plugin-manager` keeps `pluginManager` and the `plugins` Remote as a
relay: one Remote method per manager method, and one exhaustive switch
turning a failure into the Remote error of the same code (`plugins/bad-request`
into `gateway/bad-request`). Its `./types` re-exports the manager's types, so
`api-remotes` and the client import one vocabulary unchanged.
`dsh plugin add` and `remove` now run the installer instead of a bare pnpm
forwarder: every new package is probed, a package that declares neither a
bundle nor a plugin module is removed again with its reason, and newly
installed bundles are enabled as before. Other pnpm verbs still forward.
`dsh-global-tool-mask` wrapped `tools.restrict()` as a composition row so a
preset's user patch layer could hide a host tool. No shipped preset, the
plugin manager, and the settings UI compose such a row, so the package had a
README, a test, and no consumer. It goes, with its `apps/cli` dependency,
its `tsconfig.host.json` reference, the README example that named it, and the
generated catalogs. `tools.restrict()` stays: subagent tool filters call it.
Hiding a host tool from one preset is a per-preset setting for the
scope-resolved settings of the next layer, not a hand-written row; the Agent
Note records the removal under alternatives considered.
* feat(subagent): align continuable human inbox controls
* fix: repair rebase documentation and close test persistence
* docs: refresh rebased module dependency graph
Add `dsh-host-plugin-manager`: the `pluginManager` service and the
`plugins` Remote that install, uninstall, enable, disable, and retry
bundles over the booted profile, add and remove rows in the profile's
or an agent preset's user layer, report what a package's services keep
alive, and fold the manifest, the probe record, and the live tree into
one view per package. pnpm runs the way the CLI runs it, streaming its
output as `plugins/install-log`; every change emits `plugins/changed`,
and api-remotes forwards both. Enabling is the Loader's own
transaction: a rejected recomposition restores the layer list, and a
runtime recomposition records contained rows through
`recordContainedStates` because the boot audit does not run again;
`ContainedGroup.create` now records a row that resolved pending rather
than clearing it.
Give every agent preset a user patch layer: `cordis.patch.yml` beside
a locally authored composition, or alone in `.agent-presets/<id>/`
for a shipped one. Discovery attaches and judges it, the mount applies
it as the include's runtime patches, standing generations follow the
layer's content and return to a retired generation when it comes back,
the composition inventory reports each row's `source` and
`disabledBy`, a copy carries the layer, and `removeOverlay` deletes it.
`dsh-global-tool-mask` is `tools.restrict()` as a scope-only row for
such a layer.
Add `dsh-patch-file` as the one home of the patch-list format: the
parser moved out of app-boot, plus a comment-preserving key-level
writer with lock, atomic replace, and readback. app-boot exports
`layerTrust`, `bundleLayerPatches`, and `recordContainedStates`, and
`ProfileRuntime` carries the install anchor.
Mount every external bundle layer as a cordis:contained-group entry
`bundle/<package>` whose rows carry a `<package>/<id>` prefix, so one
community bundle that no longer loads records a failure in the root
`pluginFailures` registry instead of rejecting the whole Loader
transaction, and two bundles may declare the same row id. Classify
layers by origin (`builtin` for the template bundle and
`dsh.profile.firstParty`, `external` for profile dependencies) and let
`dsh.bundle.stage: boot` or `dsh.profile.stages` opt a bundle out of
isolation when it provides a service built-in rows inject; a built-in
row left pending beside an isolated bundle still fails boot and names
the bundle.
Keep `installFailLoud` for the boot transaction only and install
runtime guards afterwards: an unhandled rejection is reported and
contained, an uncaught exception is reported and exits. Report nested
fiber failures under built-in entries as advisory lines.
Provide `ctx.profileRuntime` from the launcher (layers with trust and
stage, row origin, user-disabled rows, recomposition through the root
include) and probe installed packages in a child process
(`probePackage`, cached under `profiles/<name>/.dsh-plugins/`). Split
installation from enablement in `reconcileInstalledBundles`; the CLI
keeps install-means-enable. The plugin inventory now reports `trust`,
`package`, `disabledBy`, and `failure` per row and lists rows only the
failure registry knows.
The JSONL backend rethrows storage errno failures and abort signals from the
source read of a pending migration unwrapped; a test now drives both through
the stat seam so the classification stays covered. The navigation-panes Web
e2e derives the exported archive entry from `SESSION_FORMAT_VERSION`, which
`session-log-export` uses for its `session.v<N>.jsonl` name. The module graph
records the `session-log-export` → `session` peer edge.
The persistence seam is now create/open/stat/list returning per-session
SessionHandles (read/append/flush/close); every log read and write flows
through the owning handle. The seam package exports only the service and
handle contracts, consumer-visible errors, and pure durable-data
validation helpers; each backend owns its complete storage runtime, and
the shared contract suites pin equivalent observable behavior. The
backend routes published sessions' live events by id into the active
write handle; agent-loop only acquires, seeds, and closes the handle.
Resume appends interruptedTurnClosers through its write handle;
session-query owns the revision-keyed cold cache. Legacy-only surfaces
are removed in the same swap: locate/readRaw/supportsRawArtifacts, the
legacy event-shape read migration, zstd torn-frame salvage,
DSH_SESSION_JSONL, and hook transcript_path population; a torn final
zstd frame is discarded whole; the session-list cold blank probe returns
on stat metadata (eventCount derived from the last physical row,
sizeBytes). The WebUI ZIP export serializes the logical log from a read
handle, so both backends export identically.
Refs #3245
Reverting telemetry to its own transport dropped the package's dependency
on the proxy library, and the generated graph still recorded that edge.
The gate that catches this is `verify-module-graph`, which runs in
`check:ci:static` and not in `doc-sync`; the revert was checked with the
latter alone.
Master moved 212 commits. Conflicts were eight `package.json` and nine
`tsconfig.json` files, all dependency-and-reference unions: master dropped the
`runtime-diagnostics/invariants` reference across packages while this branch
added `util/http-proxy`. The lockfile, `tsconfig.base.json`, and the module
graph were regenerated rather than merged by hand.
Master also brought `verify-package-invariants`, which rejects an empty
invariant companion. This package's companion was empty by design, so it is
gone with its publication wiring — the export, the `files` entry, the project
reference, the `dsh-invariants` peer, and the test. Master did the same across
`util/`. The README carries the reason sentence that gate requires.
Carried in the same commit, because the review arrived while the merge was open:
`e2bApiUrl` moves out of the `dsh-e2b` entry into `src/api-url.ts`. Code Entropy
flagged it as a public name with no consumer outside its own package; the seam
keeps its URL-precedence test, which is worth having directly — getting that
order wrong hands a proxy the control-plane traffic and its API key.