Merge branch 'master' into claude/unified-environment-credentials-c8841a

Master removed the TUI package, the `meta` and `upgrade` subcommands, and
`--config-replace`, and made raw `dsh` require a `--config` overlay. Resolved
onto that shape:

- Dropped this branch's TUI edits with the surface itself, including
  `tui.cordis.yml`, `runTui`, and the TUI keyless PTY smoke.
- Dropped the `--config-replace` plumbing rather than reintroducing a flag
  master deliberately removed. The gap this branch fixed remains: `dsh -p`
  still could not name its composition, so it keeps `--config`.
- Kept this branch's deletion of the personal `$DSH_HOME/config.yaml` layer,
  which master still carried, and provided the environment snapshot in the new
  raw `runConfig` surface alongside web and headless.
- Ported the headless shutdown PTY test off the personal overlay onto a named
  `--config` file, which is what proves that flag now exists on `-p`.
This commit is contained in:
Yichen Jiang
2026-08-04 17:51:44 +08:00
668 changed files with 10527 additions and 30408 deletions

View File

@@ -15,7 +15,7 @@ Shared boot glue for the app bins ([`dsh`](../../../apps/cli/README.md), [`dsh-c
| `assertEntriesActivated(ctx, binName)` | Include the `assertEntriesLoaded` check, then await every enabled entry after the Loader settles; throw with each failed plugin's original stack or each pending plugin's unresolved services |
| `loadOverlayPatches(binName, file)` | Parse a required patch-list file (a surface overlay or a `--config` file); read or parse failures throw a labelled error |
| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | Mount the statically imported Include builtin as the boot's root entry |
| `boot(binName, absoluteConfigPath, patches?, prepare?)` | Create the root context, expose `dshHomePath(...segments)` to Loader `!!js` config expressions, install Loader, run optional host preparation before config-tree entries mount (`prepare` may use Loader and provide launcher-owned context slots such as [`MAIN_SESSION_ID_KEY`](../tui/README.md)), then mount and await the include tree, assert entries loaded and activated, and return the root context — or dispose the partial context and reject a labelled error |
| `boot(binName, absoluteConfigPath, patches?, prepare?)` | Create the root context, expose `dshHomePath(...segments)` to Loader `!!js` config expressions, install Loader, run optional host preparation before config-tree entries mount (`prepare` may use Loader and provide launcher-owned context slots), then mount and await the include tree, assert entries loaded and activated, and return the root context — or dispose the partial context and reject a labelled error |
| `renderConfigDump(binName, absoluteConfigPath, layers, warn?)` | Compose the base config and labeled overlay layers offline — the include's own parser and patch algorithm (`entryListSchema`/`applyEntryPatches`), so the result equals what `boot()` mounts — and render YAML with `!!js` expressions verbatim; each run of same-provenance rows is preceded by a `# ==` comment naming the contributing file and the layers that patched it, keeping the output one loadable document; a patch matching no row goes to `warn` with its layer label (default: one stderr line), read/parse/shape failures throw |
| `addHarnessSourceSection(ctx, sourceRoot)` | Add a global `harness:source` prompt section (ordered just after the harness identity, before the persona) telling the agent the on-disk path to the DSH implementation checkout while warning it not to infer the current working directory from that path and to use `pwd` instead; a no-op returning `undefined` when the booted tree has no `systemPrompt` service. The section is registered against that service's fiber, so a dev HMR reload of the system prompt drops it until the next boot |
| `HARNESS_SOURCE_SECTION` | The `'harness:source'` section name `addHarnessSourceSection` registers under |
@@ -24,7 +24,7 @@ Loader settlement rejects import and lifecycle failures with the failing entry a
The Loader mounts entries concurrently, so a surface can already own the terminal when something else fails: exiting without the tree's own teardown would leave raw mode, bracketed paste, and the keyboard protocol set on the user's shell, and an in-flight terminal query's reply would land as literal text at the next prompt. A config-tree failure settles through `boot()`, whose disposal of the partial context runs the surface's own shutdown before the labelled rejection. For the rejections `boot()` cannot see — a plugin's detached async work rejecting during or after mounting — a terminal-owning bin passes `release` to dispose the tree before the exit commits; `dsh` captures the root context in `boot()`'s `prepare` hook rather than from its return value so the hook covers the whole mounting window. While a release is in flight the handler stays installed and latched: the first rejection is the reported one, and later rejections (teardown's own included) are swallowed rather than becoming uncaught and killing the process mid-teardown.
Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`, npm packages) resolve through the Cordis Loader's internal module loader. Repository bins install Loader's optional `node-addon-require-builtin` peer; external callers must supply it or install plugins where plain Node import resolution can find them. Relative specifiers resolve against the config directory without the native helper. The built `dsh-app-boot` artifact embeds the statically mounted Include implementation while leaving Loader external, so the include tree and host bind to one Loader peer. The `dsh` source launcher additionally maps manifest-declared workspace packages to their TypeScript source; its configuration gate requires every TUI/Web bare plugin to appear in the resolver manifest's `dependencies`. The bins' subprocess smokes exercise the internal-loader path, while this package's unit suite drives `boot()` in-process against configs with relative specifiers.
Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`, npm packages) resolve through the Cordis Loader's internal module loader. Repository bins install Loader's optional `node-addon-require-builtin` peer; external callers must supply it or install plugins where plain Node import resolution can find them. Relative specifiers resolve against the config directory without the native helper. The built `dsh-app-boot` artifact embeds the statically mounted Include implementation while leaving Loader external, so the include tree and host bind to one Loader peer. The `dsh` source launcher additionally maps manifest-declared workspace packages to their TypeScript source; its configuration gate requires every shipped raw/Web bare plugin to appear in the resolver manifest's `dependencies`. The bins' subprocess smokes exercise the internal-loader path, while this package's unit suite drives `boot()` in-process against configs with relative specifiers.
This package carries no loader hooks and no dev-mode surface. The [`dsh` app](../../../apps/cli/README.md) owns its Node source-launch hook and consumes these helpers for the boot sequence; built consumers continue to use plain Node package resolution.
@@ -32,9 +32,9 @@ This package carries no loader hooks and no dev-mode surface. The [`dsh` app](..
A developer's machine-local state lives outside every repository in the Harness home (default `~/.dsh`, overridable via `$DSH_HOME`; the single root [`resolveDshHome`](../../util/paths/README.md) resolves). What this package reads from it is one file:
- **`.env`** — the user's ordinary environment layer, loaded by the `dsh` bin through `loadLayeredEnv` beneath the invoking directory's `.env` and the inherited environment. It is plain environment with plain environment reach, not a secret boundary: what the Harness owns and isolates lives in `.credentials.yaml`, which no surface hoists. A key placed in this file therefore still resolves — as a read-only `env` layer that shadows the stored one and blocks rotation from the TUI and the web page.
- **`.env`** — the user's ordinary environment layer, loaded by the `dsh` bin through `loadLayeredEnv` beneath the invoking directory's `.env` and the inherited environment. It is plain environment with plain environment reach, not a secret boundary: what the Harness owns and isolates lives in `.credentials.yaml`, which no surface hoists. A key placed in this file therefore still resolves — as a read-only `env` layer that shadows the stored one and blocks rotation from the Web settings page.
There is no automatically discovered composition file. Loader overlays reach a surface only by being named: `dsh --config <path>` layers a patch list over the shipped tree and `dsh --config-replace <path>` boots one instead of it, on every booting surface. Keeping an overlay in `~/.dsh` is fine — it is a location, not a layer, and nothing loads it unless the launch names it ([rationale](../../../.agents/notes/implemented/simplification/2026-08-04-remove-personal-composition-layer.md)).
There is no automatically discovered composition file. Loader overlays reach a surface only by being named: raw `dsh --config <path>` applies one over the shipped base, and `dsh web`/`dsh -p` take the same flag over their shipped overlay. Keeping an overlay in `~/.dsh` is fine — it is a location, not a layer, and nothing loads it unless the launch names it ([rationale](../../../.agents/notes/implemented/simplification/2026-08-04-remove-personal-composition-layer.md)).
Subprocess test launchers point `DSH_HOME` at an isolated per-test directory so a developer's own files can never leak into fixtures.
## Model Experience