Skip to content

Releases: Big-Dao/dsh-plugin-wsl-env

v0.17.0

Choose a tag to compare

@github-actions github-actions released this 01 Nov 12:53

Changed

  • Git snapshot spawns are now agent-first with a one-shot fallback.
    Protocol v6 adds a stdin payload to the EXEC frame (a seventh header field
    flagging one trailing base64 line), which is what lets workspace-changes'
    snapshot calls — including check-ignore -z --stdin — run on the warm
    resident agent instead of a one-shot wsl.exe. Measured on ubuntu
    (2026-11-01): ~232 ms per one-shot git call (nearly all wsl.exe process
    startup) against ~1.6 ms on the resident — a 6-8 call snapshot saves well
    over a second. The per-stream caps map onto the agent's single wire cap with
    a client-side per-stream slice (lib/git-exec.js), and the environment
    rides the agent's per-request env (env -- argv prefix, no SETENV
    pollution) carrying only the translated GIT_* names. Both agents speak v6
    (the shell agent decodes the payload through command substitution, which
    strips trailing newlines — NUL separators survive; the native binary
    delivers bytes verbatim), so host library and shipped binaries upgrade
    together; the doctor's handshake reports any mismatch.

  • WSL change review no longer requires Windows git. When
    resolveExecutable('git') fails on the host, the provider answers with a
    distro sentinel (/usr/bin/git); every workspace-changes spawn — the
    rev-parse discovery included — then routes into the distro, and the
    snapshot facade translates the discovery call's absolute-path output lines
    into UNC form (translateDiscoveryOutput). A machine with Windows git is
    unchanged (host discovery + safe.directory whitelist). The
    resolveExecutable override is scoped to the one name and only fires on
    the super's failure.

  • The doctor probes the Windows git trust. A new git.win-safe check
    reports the safe.directory whitelist state through interop git.exe:
    ok when the \\wsl.localhost share is whitelisted, a warn with the
    copy-paste remedy when Windows git does not trust it, and an info (nothing
    to do) when there is no Windows git — the distro fallback covers WSL change
    review entirely.

Documentation

  • The architecture and upstream-promotion documents reflect the v6 protocol
    and the shell-menu candidates; the promotion map records the measurement
    that superseded the git one-shot decision.

  • The full real-machine suite re-ran 15/0 on 2026-11-01 after the protocol
    change landed, and again after the documentation refresh. The safe.directory
    prerequisite for WSL change review is now documented with its one-time
    remedy.

  • The shell menu now offers host shells instead of a single entry.
    terminal-controller's shellCandidates grows from [] to
    [wsl.exe, powershell, cmd] while the default profile stays
    shell: { path: wsl.exe, name: WSL }. wsl.exe dedupes into the default's
    wsl kind; powershell and cmd are launches this terminal really starts —
    host sessions already run PowerShell outright (hostSessions), and a manually
    selected shell is titled after itself, which moves the tab-title workaround
    from a user-layer edit into the plugin's own composition
    (#8769
    remains the real fix for the default entry's title).

v0.16.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 15:55

The schema-validated routing row: the root-plane router stops silently
ignoring a mistyped or wrong-typed distro key, and the first pass of the
community-optimization plan stamps the routing documentation and the 1.0
roadmap with dated, current evidence.

Changed

  • fs-routing row config is now validated at load time.
    WslRoutingFileSystem.Config no longer blindly reuses
    LocalFileSystem.Config: it is a z.intersect of the host row's schema and
    the distro-side keys (distro, wslPath, distroCwd, sandbox,
    maskWindowsDrive, restrictToDistro, watchMaxDepth). The Loader now
    refuses to mount the row when a key fails its schema — previously a typo or
    a wrong type was silently ignored and the distro side ran on defaults. A
    null field value still falls back to the schema default, which matches how
    the Loader treats optional keys. Composition tests mount the row through the
    real Loader to assert both the accepted defaults and the rejected values.

Fixed

  • docs/root-fs-routing.md claimed a 12-probe suite with no routing probe, a
    unit file outside the test lists, and a 10/10 composition test. All three
    had drifted: the suite is 15 probes with --include-fs and includes
    fs-routing.sh, test/fs-routing.test.mjs entered both test lists on
    2026-10-19 (OI-7), and the composition test is 12/12. A header note stamps
    the 2026-10-31 review.
  • docs/ROADMAP-1.0.md still described WSL-101 as implemented in the working
    tree; it shipped in 0.13.0, and the status now records which acceptance leg
    has real-machine evidence and which leg (concurrent two-distro) is still
    missing. WSL-104's "routing probe has never been executed" claim is replaced
    by the dated 2026-10-31 single-machine 15/0 result; the metrics row's probe
    count moves to the current 15-with-fs breakdown.

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 15:58

The compatibility-matrix release: the support table and its evidence, the
container and nightly legs that keep it current, two fresh-distro first-run
fixes the run surfaced, and the interop stance in one configuration key.

Changed

  • WSL-310: scripts/bootstrap-import.sh — a rootfs tarball becomes a
    plugin-ready distro in one pass.
    The command automates the import traps
    the compatibility-matrix run documented: ELF-header architecture detection
    (walking bin/ candidates, tolerant of the SIGPIPE an early-exiting dd
    causes), wsl --import with an idempotent already-exists branch, the OOBE
    wedge unstick, /etc/wsl.conf with [boot] systemd and [user] default
    (an imported default-root distro makes the agent run as root), per-family
    package-manager install including systemd-sysv — docker-export tarballs
    strip /sbin/init, which is what broke the systemd boot chain — Linger as
    root, and a final doctor pass. Verified on Debian 13.5 (systemd family:
    every check green after the one mandated wsl --shutdown) and the Alpine
    minirootfs (OpenRC: the non-systemd profile per WSL-201, systemd checks
    reported as the declared degradation).

Fixed

  • WSL-307: the confined resident's spawn preparation is now a named
    self-heal checklist.
    WSL-305 fixed the missing workspace root; the same
    fresh-distro class has more members (no sh, no base64, no sha256sum —
    minirootfs ships almost none of this). The confined factory's prepare now
    runs one sh -c round trip whose first failing step names itself
    (workspace-root, interpreter, base64, sha256sum), and the failure
    reaches the caller as spawn checklist step '<name>' failed in <distro>
    instead of a bare handshake code. The root rides as $1, never
    interpolated into the script body. Verified end to end on Debian with an
    absent workspace root: the checklist creates it and the first confined
    write lands.

Fixed

  • WSL-306: a directory name containing a newline lists as one exact row in
    the picker.
    The picker's distro-side listing was ls -1ALp line-parsed —
    a newline in a name split into two rows or dropped out entirely. The
    listing is now find -L <dir> -maxdepth 1 -mindepth 1 -type d -print0
    (NUL-separated, -type d filtering in the distro where -L resolves
    symlinks), sorted host-side. Busybox find supports every flag — verified on
    alpine:3, debian:stable-slim and fedora:latest with a newline-named
    fixture, and the picker probe now carries that fixture against the real
    composition. The LIMITATIONS entry retires.

Changed

  • The doctor gains a cross-distro sessions view (WSL-309) and marks live
    residents (WSL-313's observable half).
    A new survey after the runtime
    checks lists every running distro with its dsh-wsl-*.scope, the scope
    state and the resident pids the distro's own cgroup attests — the
    concurrent-sessions coordinate routing made possible, made visible. The
    agent.handshake check now notes when a live resident is already running in
    the surveyed distro (and that its fresh probe cannot read that resident's
    last error). The scope report records resident pids alongside the stale
    verdict. Idle countdowns and lastError stay host-side — no IPC exists, and
    the boundary is documented in LIMITATIONS.

  • The real-machine probes now run nightly on the self-hosted runner. A
    schedule trigger (21:30 UTC) fires only the probes job — every hosted job
    skips the event — so the Tier 1 "probe suite 100%, nightly" metric collects
    its evidence on its own instead of waiting for a manual dispatch. A
    dedicated concurrency group folds a backlog of missed nights (runner
    offline) into the newest run. The probes still require the harness app to
    be closed; a night where it was open reports honestly red.

  • The shell agent now runs in CI inside the non-Ubuntu containers it
    claims to support.
    test/agent-container.mjs drives the real
    wsl-agent.sh over the v5 frames inside docker run for
    debian:stable-slim, alpine:3, fedora:latest, opensuse/tumbleweed
    and archlinux — seven checks each: the HELLO handshake with the
    in-place script digest, PING, EXEC with exit code and both streams, SETENV
    reaching later commands, and the presence of sha256sum/base64 where
    the families actually differ (busybox on Alpine, coreutils elsewhere). The
    agent-containers CI job runs one container per matrix leg. 35/35 on
    first execution; this closes the container leg of WSL-202's acceptance.

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 20:51

This release fixes the first-run trap the compatibility-matrix run found on a
freshly imported distro, and ships the measured support table the run produced.

Fixed

  • WSL-305: the confined resident now creates the workspace root before its
    first spawn.
    The bwrap profile binds the workspace root and bwrap refuses
    a missing bind source, so on a freshly imported distro the first confined
    write failed with a bare FS_IO_ERROR: agent exited during handshake (code=1) and a misleading remedy. agent-confined.js gained a prepare
    hook (workspace-write only — the only mode that binds the root) that runs
    mkdir -p on the Linux-spelled root before bwrap runs, idempotently and
    ignoring its own outcome: bwrap stays the authority, and a refusal still
    reaches the caller. That refusal now carries evidence: the handshake
    failure's message includes a bounded tail of the resident's stderr (the
    half-day of diagnosis this cost came from it being swallowed). Three new
    checks cover the prepare hook, its argv, and the stderr ride-on; a
    fresh-root leg in substrate.sh exercises the absent-root spawn end to
    end on a real distro.

Changed

  • Debian is verified end to end, and the run found a first-run trap.
    The four harness-level probes pass against a wslfs-debian profile on a
    freshly imported Debian 13.5, completing the full stack on top of the
    distro-internal suite (docs/COMPATIBILITY.md). Getting there surfaced one
    real finding: the confined resident refuses to start when the workspace
    root does not exist yet — the bwrap profile binds the root, bwrap requires
    the bind source to exist, and a fresh distro has nothing to bind. Creating
    the root first turns all ten failing steps green; prior Ubuntu runs had it
    left over from earlier probes. The finding is documented in the matrix page
    with the suggested spawn-preparation fix.

  • The compatibility matrix moves from prose to a measured page.
    docs/COMPATIBILITY.md records the WSL-202 evidence: six real WSL2 distros
    (Ubuntu, Debian 13.5, Fedora 44, openSUSE Tumbleweed, Arch, Alpine 3.22)
    executed the distro-internal probe suite on one machine on 2026-10-26, and
    the page explains the two observed differences instead of hiding them — the
    session-sandbox background-survival leg races WSL session teardown on
    three systemd distros (probe artifact, not a plugin claim), and on Alpine
    the same leg fails by declaration, because non-systemd distros get the
    narrower sessionSandbox: auto scope exactly as P1.1/WSL-201 promises. The
    page also records the per-distro import traps (Debian OOBE wedge, Alpine
    minirootfs defaulting to root, busybox mktemp, zypper's interactive prompt)
    and the reproduction commands. Not yet measured: real aarch64 hardware, the
    container-image matrix.

  • The aarch64 native agent is executed in CI, not only built. The
    native packages job installs qemu's user-mode emulator and binfmt
    registration (qemu-user + qemu-user-binfmt; noble has no real
    qemu-user-static package) and runs the full
    18-check spike suite under qemu-user against both the freshly built aarch64
    musl binary and the committed agent/native/linux-aarch64/dsh-wsl-agent —
    the binary a Windows-on-ARM session would load. User-mode emulation
    exercises every check (the static musl binary and the v5 frames are
    host-OS-free), verified locally first: 18/18 under qemu-aarch64, with
    /proc/self/exe resolving to the target so the HELLO digest binding holds
    unchanged. This closes the "build-tested only" gap on WSL-202; a run on
    real aarch64 hardware remains open.

v0.13.2

Choose a tag to compare

@github-actions github-actions released this 26 Oct 08:03

This release tracks DeepSeek Harness 0.2.1-rc.3: the peer declarations become
floor ranges so a newer harness installs the plugin unchanged, and the
lockfile's transitive undici moves past a security advisory that Dependabot
could not clear on its own.

Fixed

  • undici in the lockfile moves to the security-fixed 8.12.3 via a pnpm
    override.
    undici rides in only as an exact transitive pin of
    @deepseek-ai/dsh-http-proxy — a package upstream controls — so Dependabot's
    security job could resolve no patched version (it reports "the latest
    possible version … is 8.11.2" and fails). pnpm-workspace.yaml now carries
    overrides: { undici: '>=8.12.3' } (the pnpm 11 settings home; the
    package.json pnpm field is no longer read), the lockfile swaps the one
    package, and the override comment records that it is retired when upstream
    moves the pin. Nothing under src/ reads undici; the check chain is
    unaffected.

Changed

  • The peer declarations become floor ranges, so a newer harness installs
    the plugin unchanged.
    The @deepseek-ai/dsh-* entries in
    peerDependencies move from the exact pin to >=0.2.1-rc.3, and
    @deepseek-ai/cordis / @deepseek-ai/schemastery widen from ~ to ^
    (^4.0.5, ^3.18.5). An app that ships a newer harness release now
    satisfies the peers without waiting for a plugin release — same-line rc
    bumps and every stable release install cleanly; only a brand-new
    prerelease line (0.2.2-rc.1 against >=0.2.1-rc.3) still warns, because
    semver admits a prerelease only against a comparator on its own tuple and
    no range can promise an unpublished line. That residue is what the weekly
    peer-canary is for: it still resolves the next dist-tag into a scratch
    tree and flags when the line moves past a floor — the signal to re-run the
    docs/PEER-PARITY.md comparison and raise the floor in the commit that
    matches the harness release. The devDependencies keep their exact pins, so
    the gates stay reproducible and the parity audit keeps naming one line.
    scripts/doctor.mjs learned the new shapes (peerSatisfied now reads >=
    and ^, and its compareVersions orders prerelease identifiers by semver
    §11 — rc.10 above rc.9), scripts/peer-canary.mjs parses and preserves
    the shapes, and AGENTS.md, CONTRIBUTING.md, SUPPORT.md and
    docs/PEER-PARITY.md state the new policy.

  • The dev pin line moves to DeepSeek Harness 0.2.1-rc.3. Every
    @deepseek-ai/dsh-* pin in devDependencies goes from 0.2.1-rc.2 to
    0.2.1-rc.3 — the release the next dist-tag resolves to — and
    pnpm-lock.yaml refreshes with it. @deepseek-ai/cordis and
    @deepseek-ai/schemastery stay at 4.0.5 / 3.18.5: upstream's vendor/
    did not change on this line.

    No module under src/ changed, and that is the finding. The three peers
    this plugin subclasses or replicates ship byte-identical lib/index.js
    files on rc.3 — dsh-fs-local (the fsio dialect, still 909 lines), dsh-fs
    (the FsVersion brand) and dsh-bash-local (the spill machinery) — so
    every line reference in docs/PEER-PARITY.md still names the same code;
    only the tarballs' digests changed, and the parity doc now carries the
    0.2.1-rc.3 ones with the re-check dated 2026-10-26. That byte-identity is
    also the evidence behind the new peer floor: the same code answers above
    rc.3. Upstream's release notes for the line are UI- and LLM-side
    (navigation entry customization, analytics, the official /models
    listing): the one composition-visible change is a new entry-customization
    UI row in the app's own cordis.patch.yml, which rides with the app's
    bundle rather than this plugin's layer. preset-wsl needed no
    regeneration — packages/bundle/web-app/presets/standard.patch.yml is
    byte-identical on rc.3 — and the provenance comment in cordis.patch.yml
    now names the rc.3 tree.

v0.13.1

Choose a tag to compare

@github-actions github-actions released this 19 Oct 12:19

A patch release restoring wsl-shell-env: the component mounts again, and the
managed DSH_* facts it contributes now reach commands on the default
resident-agent path.

Changed

  • Upstream proposals filed. The session-notice seam
    (#10263),
    the spawnSpec seam (#10261), the fsio export (#10262), the UNC-workspace bug
    (#10260) and the WSL-workspaces RFC (#10264) now point at their discussions;
    the local drafts carry the links.

Fixed

  • wsl-shell-env no longer fails to activate. The row sweeps the
    live-agent list at mount to warm facts for sessions that opened before it,
    but declared only shellEnv in inject and read ctx.agents as a property.
    Cordis 4 refuses that read — cannot get property "agents" without inject —
    so apply() rejected and the plugin manager listed the component as 异常
    (包含的组件 → 异常), with the whole contribution missing from shell calls.
    The sweep now reads the service through ctx.get("agents"), the sanctioned
    access for a service this row does not inject: activation no longer depends
    on an agent loop, and the sweep still runs when one is composed.
    wsl-preflight carried the same property read inside a try/catch, where
    the throw silently disabled its mount sweep; it gets the same fix.
    test/shell-env.test.mjs and the real-cordis check in
    test/preflight.test.mjs mount both rows against the guard.

  • The managed DSH_* namespace now reaches commands on the default
    resident-agent path.
    ShellExecSpec.dshEnv was forwarded only by the
    one-shot wsl.exe fallback; executeViaAgent dropped it before
    agent.exec — after the mount fix above, the row registered its facts but
    no model shell call could see DSH_HOME, DSH_SESSION_ID or any
    DSH_WSL_* value (env | grep '^DSH_' was empty). The handle now carries
    the merged map (env first, dshEnv last — the same order the one-shot
    path uses) into the agent's SETENV frames. test/agent-exec.test.mjs and
    test/provider.test.mjs pin the frame, and both checks fail against the
    previous build.

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 07:41

This release ships the P0/P1 hardening batch: session-scoped auto
sandboxing, resident-agent recovery with doctor coverage, the packed-install
E2E, measured GUI-preview and @-reference probes, and the docs/community
entry points.

Added

  • pnpm run preset — the preset-wsl override helper. wsl-shell and
    wsl-fs sit inside preset-wsl's config.plugins, so a patch that targets
    them by id is ignored with only a loader warning. The helper prints the
    complete preset-wsl block, applies distro/sessionSandbox/sandbox/
    maskWindowsDrive/agentBackend, and --check fails loudly with the exact
    block to paste when a layer carries the nested id patch. See
    docs/CONFIGURATION.md.

  • Session-notice upstream payload ready. The exact title, body and gh
    command for the session-visible notice proposal are in
    docs/upstream/proposal-session-notice.md; it remains unfiled (a maintainer
    action).

  • Configuration stability and migration policy. docs/MIGRATION.md records
    the below-1.0 freeze (new keys preserve behavior, removals refuse at boot with
    a migration, meaning changes get a note), the deprecation path with the
    substrate: "share" and distro: '' cases, and the release-gate table with
    the manual legs (probe suite, clean machine, aarch64) marked unrun.
    RELEASING and SUPPORT link it.

  • Upstream promotion pack documented. docs/upstream/promotion-pack.md
    records the packages/wsl/* layout (ten capability packages plus an
    integration package), the seam/RFC index, what the composition boot test
    covers and its gaps, and the install proof. Promotion stays packaging and
    proof, not a dependency.

  • @-reference traversal cost measured; seam deferred. The Windows walk
    over the WSL share is ~56× the ext4 walk on a 520-entry tree (84.7 ms vs
    1.5 ms) and is now reproducible with pnpm run probe:file-reference.
    docs/UPSTREAM-FILE-REFERENCE-SEAM.md records the three-function seam, the
    ready (unwired) plugin consumer, and the decision to keep the 9p walk until
    upstream exposes the funnel.

  • Community loop: issue forms, triage targets and a contributor path.
    Feature-request and setup-report forms (the setup form requires
    doctor --json), a chooser that disables blank issues, SUPPORT's P1
    three-business-day target and two-to-four-week cadence, and a
    good first issue path in CONTRIBUTING.

  • Quickstart and performance entry pages. docs/QUICKSTART.md is the
    five-minute path (prerequisites, install, first session, Node/Python/Rust/Go
    and monorepo recipes, honest Tier 1/Tier 2 table); docs/PERFORMANCE.md
    records the 9p-vs-agent numbers and the GUI-preview baseline with reproduction
    commands. The visual demo is explicitly still missing.

  • GUI-preview latency has a measured baseline. pnpm run probe:gui-preview
    drives the routed backend's stat, listDir and 1 MiB readByteRange with
    p50/p95 budgets (stat 100 ms, listing 500 ms, page 1 s/MiB). On this machine:
    p95 2.7 ms / 5.1 ms / 63.5 ms per MiB. No optimization follows; the
    workspace-files row is untouched.

  • LSP seam researched and deferred. docs/UPSTREAM-LSP-SEAM.md records that
    the harness ships dsh-lsp/dsh-lsp-stdio/dsh-tool-lsp but the 0.2.1-rc.2
    base composition mounts none of them, and that a WSL provider needs a
    preset-scoped subprocess provider plus URI translation. The prototype is
    deferred behind an explicit trigger; no LSP claim is made in the README.

  • Peer-upgrade canary. A weekly workflow resolves the next dist-tag of
    every pinned peer, tests that line in a scratch copy (pnpm test + the
    composition boot test), and publishes the exact version delta in the run
    summary; a moved line that fails fails the run. pnpm run canary:peers
    performs the check locally.

  • The strict profile is documented and probed. maskWindowsDrive: true with
    sandbox: true hides /mnt from confined profiles; CONFIGURATION describes
    the trade-offs and the visible/masked/disabled interop matrix, and
    probe:sandbox now asserts the masked leg (empty /mnt, unreachable Windows
    binary, writable workspace). Enforcement stays partial; the binfmt content
    escape and the distro-level closure are stated.

  • Install/upgrade/uninstall E2E. pnpm run e2e:install packs the tree,
    installs it into a fresh %TEMP% DSH_HOME, composes --dump-config, removes
    it, and upgrades from the previous minor with a marker proving the profile
    layer survived. test/packaging.test.mjs keeps the packed file list honest in
    pnpm test.

  • The doctor probes the resident agent. agent.handshake starts the
    shipped shell agent in the distro, reads its HELLO (pid, protocol, script
    digest) and stops it, so a missing or stale agent script is a named check
    instead of a first-command failure. It also reports the agent cache and the
    live/stale dsh-wsl-*.scope counts.

  • pnpm run doctor — one command for the environment prerequisites. Reports
    Windows/WSL versions, the distro and its login shell, bubblewrap usability,
    systemd / the user manager / systemd-run / Linger, the optional tools, peer
    versions, the native-agent cache, stale dsh-wsl-*.scope units and the
    profile-patch trap that silently ignores wsl-shell/wsl-fs overrides. Each
    failure carries one copy-paste remedy; --json is the machine shape, and
    --fix only sweeps stale scopes unless --install plus a confirmation allows
    a package install. diagnose is now the read-only alias.

Changed

  • sessionSandbox: auto — the session runtime is an optimization, not a
    prerequisite.
    wsl-shell and wsl-fs accept true, false or "auto";
    auto uses the systemd user scope when it is available and otherwise falls
    back to the per-command bwrap profile — still confined, never unconfined —
    reporting sessionScope: "session" | "per-command" in the command result.
    A failed verdict is re-probed at most once a minute, so installing systemd or
    enabling Linger upgrades without an app restart. true keeps refusing with
    the existing remedy; false is unchanged.

  • The session preflight also checks the session-runtime prerequisites. It
    now probes systemd as PID 1, the user manager, systemd-run and Linger —
    not only bubblewrap — when a distro session opens, and publishes the same
    advisory to the session's runtime context as well as the log. sessionSandbox: false on the wsl-preflight row skips the session half. DSH 0.2.1 has no
    session-notice seam; the ask is recorded in
    docs/UPSTREAM-SESSION-NOTICE.md and the
    boundary in docs/LIMITATIONS.md.
    Advisory only: nothing is installed and fail-closed behavior is unchanged.

  • The distro follows the workspace. distro: ''/'auto' now selects the
    distro from the request workdir or target UNC; 'default' forces WSL's
    default, and an explicit name still pins. Shell, files, root-plane routing,
    the GUI terminal and shell-env all use the same rule, and the shared agent,
    substrate, home and session-runtime state are cached per distro. A pinned
    profile still refuses a foreign UNC with FS_OUTSIDE_DISTRO. This changes the
    previous meaning of distro: '' (WSL's default): set 'default' to keep that
    posture.

Fixed

  • A dead resident revives with bounded backoff instead of staying dead for
    the process lifetime.
    A failure that spends the one permitted rebuild now
    leaves the agent dead with a revival scheduled (2 s, 4 s, 8 s); the next
    call after the delay gets a fresh transport and its own single rebuild, and
    nothing from the dead generation is replayed. After three deaths the state is
    permanent and the refusal names the restart remedy. status() exposes the
    live state, pid, last error and pending count for diagnostics.

  • A cancelled command's signal can no longer kill the next one. The
    out-of-band USR1 carries no request id: a signal meant for a timed-out
    command could land after the agent had moved on, and the next command then
    died with exit 138 but reported completed. A host timeout no longer sends
    the signal at all (the in-distro watcher is the one that kills that
    command), and an explicit cancel now waits briefly and is dropped once the
    request answered or a newer request is running.

  • A missing workdir is reported with its path on the native agent. The
    native agent reports the failed spawn as reason spawn, without naming the
    directory; the executor now treats that as its own error class and retries
    the command on the one-shot path, whose chdir(<path>) failed warning names
    the directory.

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 02:19

The GUI file service keeps the shipped workspace-files row: its server-only
replacement dropped the browser file resource provider and broke Sidebar
previews. The shipped row now serves WSL paths through fs-routing. The
wsl-shell.maskWindowsDrive wiring fix ships with it.

Fixed

  • wsl-shell.maskWindowsDrive reaches the per-command sandbox. The shell
    executor built its WslSandbox without the configured key. With
    sessionSandbox: false the per-command profile therefore left /mnt
    visible after the operator asked for the mask; with sessionSandbox: true
    the runtime still masked the drive, but the decorated result reported
    windowsDrive: "visible". The executor now passes the value, matching
    wsl-fs and fs-routing.

  • Sidebar previews work: the GUI file service keeps the shipped
    workspace-files row.
    The former workspace-files-wsl takeover replaced
    that row with a server-only subclass, which dropped the browser file
    resource provider and made every Sidebar preview report "The file resource
    service is unavailable." The shipped row now serves WSL paths through
    fs-routing, so the server and client halves stay paired. The trade-off is
    GUI-only: a listing, stat or page read takes roughly four to six agent round
    trips instead of one, and a deep page read streams the preceding lines through
    the pipe. The model's file tools, search and @ references are unaffected.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 15:54

The shared read agent is the native binary (A7): unconfined reads now use the
packaged static-musl agent with the shell as the fallback, cutting 16
concurrent reads from 342-614 ms to 2.4-8.2 ms. The A6 FS measurement and
metrics ship with it.

Added

  • The shared read agent is native (A7). WslAgent gained an async
    prepare() hook; sharedAgent uses it to select the packaged native binary
    through agentBackend (auto/native), with the shell as the fallback
    (shell, a failed preparation, or a native HELLO/runtime failure; the reads
    never fail closed). The A6 probe measures 16 concurrent shared reads at
    2.4-8.2 ms, down from 342-614 ms on the shell. The native shared agent also
    serves the picker, the subprocess activity probes and the non-session
    command path, so those inherit EXEC concurrency.

  • WslAgent.metrics(). Per-kind queue wait (sent -> ACK), execution time
    (ACK -> settle), in-flight peak, rejected and cancelled counters; the
    47-check lifecycle suite covers the counters and the reset.

  • test/probe/fs-queue.mjs. A live measurement of both FS agents. The A6
    run showed the shared shell reads were the batch bottleneck (16 concurrent
    stat/read/list at 342-614 ms, queue up to 583 ms) while native FS ops
    execute in 0.2-0.3 ms; the A7 run measures the native shared reads at
    2.4-8.2 ms. v6's lock removal only takes 16 native writes from 7.7 ms to
    ~5 ms, so it stays deferred.

Changed

  • The plan after the A6 measurement: A7 (native shared reads) landed, v6
    deferred.
    docs/ROADMAP.md records the done, next and
    deferred steps; ADR 0002, LIMITATIONS and the READMEs describe the native
    shared reads and the v6 deferral.

  • test/probe/fs-queue.mjs and the README/LIMITATIONS/ROADMAP describe the
    native shared reads; test/agent-shared.test.mjs covers the shell selection.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 14:57

The native in-distro agent is the default session-runtime backend: agentBackend: auto
selects the packaged static-musl binary for the distro's architecture, the shipped
shell agent stays as the fallback, and native/shell keep the strict and forced
modes. The peer line moves to DeepSeek Harness 0.2.1-rc.2, which is the desktop
app's own version line. No module under src/ changed, and that is the
finding, not an omission: every peer artifact this plugin subclasses or
replicates is byte-identical to the one shipped for 0.2.0-rc.2, except one
added field in the dsh-api-workspace-files class that WorkspaceFilesWsl
extends (and that package's regenerated typert artifacts), so the seams the
code is built on did not move. What did move is the shipped composition — the
standard agent preset — and the plugin's embedded preset-wsl is regenerated
from it.

Added

  • The watcher uses inotify when it can. With inotifywait installed and an
    unbounded directory target, the in-distro watcher now uses recursive inotify
    watches and coalesces events to one callback per 0.5s instead of polling
    find. A file target, a positive watchMaxDepth, or a missing
    inotify-tools keeps the polling fallback, so the backend is optional. The
    polling mtime-old blind spot does not apply to the inotify path.

  • sessionSandbox runs one session's commands in one scope. With the key
    on, the shell executor acquires the runtime for (distro, workspace, policy) on the first command, reuses it for the session, runs the command
    WITHOUT the per-command bwrap wrap (the runtime's own mounts confine it),
    and releases it from the preset scope's disposer — closing the agent and
    stopping the unit, whose cgroup reaps the session's leftovers. On in the
    shipped preset; requires agent: true, systemd as PID 1, a reachable user
    manager, systemd-run and Linger. The preflight refuses with the remedy
    when one is missing (only a success is cached, so following the remedy
    needs no app restart); set sessionSandbox: false for the per-command
    shape. Validated on a live session: ps -p 1 is systemd, the command's
    cgroup is the dsh-wsl-*.scope, a background process and /tmp survive
    across commands, and the unit disappears after the session ends.

  • The session-runtime registry. src/session-runtime.ts refcounts one
    agent per (distro, workspace, policy), composes the systemd user scope
    prefix with a bwrap mount profile that avoids a PID namespace (and binds the
    real /tmp under workspace-write), and tears a runtime down by closing the
    agent and then stopping the scope. --unshare-pid is now a profile option
    (default true), and the agent accepts an explicit lease independent of the
    idle close. Tests cover the refcount, the mode-specific mounts, the mask in
    the key, the explicit lease, and the close-then-stop order. It is wired into
    the shell executor behind sessionSandbox, with a preflight that refuses
    with a remedy; the filesystem provider's unification is next.

  • The session-scoped sandbox's launcher primitives. src/session-scope.ts
    builds the systemd user unit name, the systemd-run --user --scope prefix,
    the stop argv and the preflight probe argv; unit tests pin the shapes and the
    sanitization. ADR 0001 records the runtime-ownership decision the wiring
    still has to make (the agent is a per-distro singleton today). No behavior
    change yet.

  • docs/VALUE.zh.md states what the plugin is worth, in Chinese, for a
    reader deciding whether to install it: the three misalignments it removes,
    six values each with its mechanism and the evidence that backs it, the
    comparison against no integration / desktop remoting / putting the CLI in
    WSL, the costs and limits stated before the sale, how the claims are
    proven, and a rule for when not to use it. It is linked from both readme
    indexes and sits beside the reference docs, never in place of them.

Changed

  • The peer pins move to the 0.2.1-rc.2 line: @deepseek-ai/dsh-* at
    0.2.1-rc.2, @deepseek-ai/cordis at ~4.0.5, @deepseek-ai/schemastery
    at ~3.18.5, with pnpm-lock.yaml refreshed. pnpm's release-age policy
    records the exact closure it had to exempt in pnpm-workspace.yaml, the way
    the harness repository does. The three seam packages were compared byte for
    byte against the previous pin: dsh-fs-local's lib/index.js (the fsio
    dialect this plugin replicates), dsh-fs's, and dsh-bash-local's (the
    spill machinery and the spawnSpec seam) are identical files, so every line
    reference in docs/PEER-PARITY.md and docs/UPSTREAM-SPAWN-SEAM.md still
    names the same code — only the tarballs' digests changed, and
    docs/PEER-PARITY.md now carries the 0.2.1-rc.2 ones. cordis@4.0.5 and
    schemastery@3.18.5 likewise publish the same bytes as 4.0.4 and
    3.18.4 except for their manifests. Elsewhere: dsh-fs@0.2.1-rc.2 drops its
    unpublished ./invariant export subpath (nothing here imported it),
    dsh-subprocess-local's new code is Linux systemd scope selection (this
    plugin spawns wsl.exe from a Windows host), dsh-api-workspace-files adds
    one resource field to its own lookup registration, and the typert protocol
    gained modules.
  • preset-wsl is regenerated from the shipped standard preset. The rows
    that arrived are the clock-reading time-context row, tool-schedule, and
    the disabled tool-plugin-market/tools row; the rows that left are
    tool-ralph and the disabled subagent_codex / subagent_claude_code
    entries, whose providers the preset no longer declares anywhere. The two
    tool-subagent rows drop backgroundMode, the plan-mode section's first
    sentence now says the plan is approved through exit_plan_mode, and
    persona.suffix is gone: the host plane's new working-directory row
    reports the Session's cwd, so the "Your working directory is …" suffix is no
    longer the mechanism. The three deliberate deviations from the shipped copy
    — the wsl-env realm wrapping every row, the fixed shell-tool choice, and
    the preset's identity — are unchanged, and the transformation is now
    scripts/build-preset-wsl.mjs rather than the
    machine-local generator the block used to name: it takes the shipped file and
    prints the row, and its output is the committed block byte for byte.
  • docs/UPSTREAM-TERMINAL-FONT.md closes: the 0.2.1-rc.2 line made the
    terminal font a user setting (--dsh-font-family-terminal /
    --dsh-terminal-font-size), so the hardcoded stack it argued against is now
    only the fallback appended after the user's list.
    docs/UPSTREAM-TERMINAL-TITLE.md was re-checked against the new tag and is
    unchanged, including after the terminal controller's 0.2.1 rework: the tab
    title is still built from shell.name alone.

Fixed

  • The GUI file service loads again from a linked install (workspace-files-wsl
    failed to import, and the Desktop host then died on a write EOF). The module
    imports @deepseek-ai/dsh-api-workspace-files and
    @deepseek-ai/dsh-typert-protocol at runtime, but both were declared only as
    dev dependencies. The runtime's linked-plugin resolver routes a bare import
    to the installation package only when the plugin's own manifest lists that
    name under peerDependencies (dsh-app-boot's readPeerNames /
    routeLinked), so the import missed, the entry never activated, and Desktop
    exited 1; the fatal write EOF is the shell writing to that already-exited
    host. Both names are peers now, and test/peers.test.mjs scans every
    lib/*.js import so an undeclared runtime package fails the check chain
    instead of the app.

  • A warm native-agent cache no longer kills the Host on startup. The
    in-distro copy script prints the cache path and exits without reading stdin
    when the digest already matches. copyBinary still streamed the packaged
    binary into that closed pipe and left stdin's error event unhandled; on
    Windows the write surfaced as write EOF and took the Host down before the
    UI could load, so the first run (cold cache) worked and every later start
    crashed. The write error is absorbed now — the exit code and stdout already
    decide the result — and a POSIX regression check runs an early-exit script
    against a payload larger than any pipe buffer.

  • The root-plane router survives its first distro target, and that path now
    has a regression test.
    Three defects sat behind the same runtime failure:

    • WslRoutingFileSystem kept its state in ES #private fields. Cordis
      invokes a service method with a shadow receiver (the traceable proxy
      ReflectService builds), and private members do not cross it, so the first
      routed call threw Cannot read private member #distro from an object whose class did not declare it. The fields are plain now, exactly like the host
      backend's own config and locks.
    • Constructing the distro backend with the router's context re-registered
      fs in the same scope (service "fs" has been registered). The backend
      now receives an fs-isolated child scope, while sandboxPolicy still
      resolves from the parent. The router captures its construction context,
      because this.ctx inside a method is the caller's context, not its own.
    • The row dropped the static inject = ["sandboxPolicy"] declaration the
      shipped fs-sandbox row carries, although the backend it constructs reads
      ctx.sandboxPolicy.defaultMode. That service is provided by a sibling row,
      not an ancestor, so the fiber could not resolve it and construction failed
      with cannot get property "sandboxPolicy" without inject. The declaration
      is restored.

    The 0.2.1-rc.2 line's new working-directory row resolves the session cwd
    through the root ctx.fs, which is what first exercised the distro branch.
    test/composition.test.mjs now bo...

Read more