dsh-plugin-wsl-env 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -1
- package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
- package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
- package/agent/wsl-agent.sh +47 -7
- package/cordis.patch.yml +15 -9
- package/docs/ARCHITECTURE.md +14 -6
- package/docs/CONFIGURATION.md +23 -0
- package/docs/CONFIGURATION.zh.md +288 -0
- package/docs/LIMITATIONS.md +133 -299
- package/docs/LIMITATIONS.zh.md +161 -0
- package/docs/QUICKSTART.md +2 -0
- package/docs/QUICKSTART.zh.md +168 -0
- package/docs/UPSTREAM-TERMINAL-TITLE.md +10 -12
- package/lib/agent-exec.d.ts +5 -0
- package/lib/agent-protocol.d.ts +18 -5
- package/lib/agent-protocol.js +11 -6
- package/lib/agent.d.ts +9 -1
- package/lib/agent.js +2 -2
- package/lib/git-exec.d.ts +117 -0
- package/lib/git-exec.js +105 -0
- package/lib/git-route.d.ts +43 -13
- package/lib/git-route.js +54 -15
- package/lib/subprocess.d.ts +21 -7
- package/lib/subprocess.js +63 -9
- package/package.json +4 -1
- package/scripts/doctor.mjs +20 -0
package/docs/LIMITATIONS.md
CHANGED
|
@@ -1,336 +1,170 @@
|
|
|
1
1
|
# Limitations
|
|
2
2
|
|
|
3
|
+
*A Chinese translation lives at [LIMITATIONS.zh](LIMITATIONS.zh.md); the English version is authoritative.*
|
|
4
|
+
|
|
3
5
|
What the plugin does not do, and why. A limitation that is documented is a design
|
|
4
|
-
boundary; one that is not is a bug report waiting to happen.
|
|
6
|
+
boundary; one that is not is a bug report waiting to happen. Each entry states the
|
|
7
|
+
limit and the remedy; the reasoning lives in [ARCHITECTURE.md](ARCHITECTURE.md) and
|
|
8
|
+
the documents it links.
|
|
5
9
|
|
|
6
10
|
## The filesystem substrate (`wsl-fs`)
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- **
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
agent syncs the staged file before publication; the shell agent attempts
|
|
44
|
-
`sync -f` but ignores failure. Neither syncs the containing directory after
|
|
45
|
-
the rename or link. A kernel panic or power loss can therefore lose data or
|
|
46
|
-
the published directory entry; the operation is not a crash-consistent store.
|
|
47
|
-
- **A Linux filename containing a backslash cannot be addressed.** The UNC
|
|
48
|
-
display form (`\\wsl.localhost\<distro>\...`) is the file's identity
|
|
49
|
-
everywhere - the session header, the GUI, the caches - and the backslash is
|
|
50
|
-
the UNC's separator, so `a\b.ts` in the distro round-trips to `a/b.ts`: the
|
|
51
|
-
tools would silently address a different path. This is the coordinate
|
|
52
|
-
system's own limit — a backslash-delimited identity cannot spell a name
|
|
53
|
-
containing a backslash — not a defect of the substrate; a rename away from
|
|
54
|
-
backslash names is the workaround.
|
|
55
|
-
- **Stage two routes mutations through a confined resident.** A write or edit
|
|
56
|
-
under a confined policy runs on its own long-lived agent, spawned inside the
|
|
57
|
-
bwrap profile that binds exactly what the mode grants — so the kernel, not
|
|
58
|
-
just the host-side check, refuses a write outside the workspace, and the
|
|
59
|
-
refusal carries `FS_SANDBOX_DENIED` (the command path's dialect). Costs to
|
|
60
|
-
know: `bubblewrap` becomes required for confined writes (a distro without it
|
|
61
|
-
refuses them with the bootstrap command, rather than downgrading); a policy
|
|
62
|
-
change addresses a different resident, so the first write after switching the
|
|
63
|
-
Permissions selector pays one agent startup; and a write the policy grants to
|
|
64
|
-
the distro's `/tmp` lands in the distro's real `/tmp`, because the
|
|
65
|
-
file-writes resident binds it read-write — where the read tools, the plain
|
|
66
|
-
resident and the user's own shell all see it. The ephemeral tmpfs belongs to
|
|
67
|
-
the command side alone, so a file handed over from a command still goes
|
|
68
|
-
through the session workspace.
|
|
69
|
-
- **`glob` and `grep` run the distro's own rg** (`lib/search-route.js`): the
|
|
70
|
-
tool's arguments are forwarded verbatim, so results, output format and exit
|
|
71
|
-
codes are rg's own — at ext4 speed, with the distro's `.gitignore` rules.
|
|
72
|
-
A distro without rg fails the search outright: exit 127, with the message
|
|
73
|
-
from the exec launcher and never from rg itself (which is the missing
|
|
74
|
-
program) — `setsid: failed to execute rg: No such file or directory` where
|
|
75
|
-
the distro has `setsid`, the script shell's `rg: not found` where it does
|
|
76
|
-
not. The install command `scripts/bootstrap.sh` prints — `wsl.exe -d <distro>
|
|
77
|
-
-u root -- apt-get install -y bubblewrap git ripgrep inotify-tools`, or the
|
|
78
|
-
detected family's spelling — installs it. `--install` runs that command only
|
|
79
|
-
when `bubblewrap` itself is missing, so a distro that has bwrap but not rg
|
|
80
|
-
needs the printed line run by hand. A search whose directory is a Windows
|
|
81
|
-
folder keeps the host binary, which is native there.
|
|
82
|
-
- **`watch()` prefers inotify, with a polling fallback.** When `inotifywait`
|
|
83
|
-
is installed and the target is an unbounded directory, the watcher uses
|
|
84
|
-
recursive inotify watches and coalesces events to one callback per 0.5s.
|
|
85
|
-
Otherwise — a file target, a positive `maxDepth`, or no `inotify-tools` — it
|
|
86
|
-
is the `find -newer` poll loop (a `stat` fingerprint for a file), so the
|
|
87
|
-
coarse-invalidation caveats below apply.
|
|
88
|
-
- **With `sessionSandbox: false`, a confined command's `/tmp` is not the write
|
|
89
|
-
tool's `/tmp`.** `workspace-write` mounts a fresh tmpfs at `/tmp` for the
|
|
90
|
-
command — that is what makes it ephemeral — while the file tools' fence grants
|
|
91
|
-
the distro's *real* `/tmp`. Both are writable, so neither side is refused, but a
|
|
92
|
-
file the write tool puts in `/tmp` is invisible to `bash` and vice versa. Hand
|
|
93
|
-
a file between them through the session workspace. The shipped preset turns
|
|
94
|
-
the session runtime on, and `wsl-fs` shares it for confined writes, so
|
|
95
|
-
commands and writes bind the same real `/tmp` and workspace.
|
|
96
|
-
- A distro that is stopped or unregistered while the app runs takes the file
|
|
97
|
-
tools' substrate with it: reads and writes report the substrate-unavailable
|
|
98
|
-
error naming the cause. A death with nothing in flight costs nothing — the
|
|
99
|
-
next call starts a fresh transport, so a distro that comes back is served
|
|
100
|
-
again. A death with a request in flight spends the resident's one permitted
|
|
101
|
-
rebuild, and when that rebuild cannot complete — the distro is still down, or
|
|
102
|
-
gone — the resident is permanently out: every later file operation fails the
|
|
103
|
-
same way until the app restarts.
|
|
104
|
-
- `watch()` observes from inside the distro, so callbacks are coarse
|
|
105
|
-
invalidation, not per-file notifications. With inotify they arrive as events
|
|
106
|
-
(coalesced to 0.5s); with the polling fallback they arrive on the poll
|
|
107
|
-
cadence, not instantly. In the polling path a creation is seen even when the
|
|
108
|
-
new file's own mtime is old: creating it bumps the containing directory's
|
|
109
|
-
mtime, and the scan includes the watched directory itself (deletions are seen
|
|
110
|
-
the same way). What escapes the polling stamp is a change that leaves every
|
|
111
|
-
scanned mtime older than it — an in-place `cp -p` onto an existing file (new
|
|
112
|
-
content, a copied-back mtime), or a `tar -x` whose archive restores the
|
|
113
|
-
containing directory's own mtime. That blind spot does not apply to the
|
|
114
|
-
inotify path. A FILE target is watched by its `mtime:size` fingerprint: a
|
|
115
|
-
rename-over or an in-place write fires, a removal reports the target as gone,
|
|
116
|
-
and a change that preserves both mtime and size escapes it. Watching a 9p
|
|
117
|
-
share from the Windows side remains unsupported — the loop runs where the
|
|
118
|
-
kernel can actually report change.
|
|
12
|
+
One substrate: the resident in-distro agent (ext4 for distro files). The retired
|
|
13
|
+
Windows-side share backend (`substrate: "share"`) is refused at boot; WSL-routed
|
|
14
|
+
operations never use the 9p share.
|
|
15
|
+
|
|
16
|
+
- **A new file publishes with mode 0600.** Overwrites and edits keep the original
|
|
17
|
+
bits; `chmod` when a new file must be executable.
|
|
18
|
+
- **When the agent is out, file operations fail closed** — `FS_IO_ERROR` naming the
|
|
19
|
+
substrate, with the recovery path. One crash rides a single rebuild; a spent
|
|
20
|
+
rebuild enters bounded revival (2/4/8 s); three deaths are permanent until the app
|
|
21
|
+
restarts. There is no fallback I/O — deliberately.
|
|
22
|
+
- **The mutation guard is not atomic compare-and-swap.** Creates use a no-replace
|
|
23
|
+
link; overwrites re-verify the version before the rename. A mismatch returns
|
|
24
|
+
`FS_STALE_VERSION`, but a change between check and rename can still be overwritten.
|
|
25
|
+
- **A cancelled write can have an unknown outcome** — inspect the file before
|
|
26
|
+
retrying.
|
|
27
|
+
- **A successful write is not crash-durable.** The staged file is synced, the
|
|
28
|
+
containing directory is not: a power loss can lose the entry.
|
|
29
|
+
- **A Linux filename containing a backslash cannot be addressed** — the backslash is
|
|
30
|
+
the UNC separator, so `a\b.ts` round-trips to `a/b.ts`. Rename away from backslash
|
|
31
|
+
names; this is the coordinate system's own limit.
|
|
32
|
+
- **Confined writes run on their own bwrap-confined resident** — kernel-enforced,
|
|
33
|
+
refusing outside the workspace with `FS_SANDBOX_DENIED`. Costs: `bubblewrap`
|
|
34
|
+
required; a policy change pays one agent startup; a write granted to `/tmp` lands
|
|
35
|
+
in the distro's real `/tmp`.
|
|
36
|
+
- **`glob`/`grep` run the distro's rg**; a distro without rg fails the search
|
|
37
|
+
(exit 127). Install: `wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap
|
|
38
|
+
git ripgrep inotify-tools` (or the detected family's spelling). `--install` runs it
|
|
39
|
+
only for a missing `bubblewrap`.
|
|
40
|
+
- **`watch()` is coarse invalidation, not per-file notifications**: inotify events
|
|
41
|
+
coalesced to 0.5 s, or a `find -newer` poll loop otherwise. The polling path misses
|
|
42
|
+
an in-place `cp -p` or a `tar -x` that restores older mtimes; a FILE target watches
|
|
43
|
+
an `mtime:size` fingerprint. 9p watching from Windows stays unsupported.
|
|
44
|
+
- **With `sessionSandbox: false`, a confined command's `/tmp` differs from the write
|
|
45
|
+
tools' `/tmp`** — hand files over through the session workspace. The shipped
|
|
46
|
+
preset (`sessionSandbox: true`) shares one real `/tmp` and has no such split.
|
|
119
47
|
- `editText` reads the whole file into memory before writing it back.
|
|
48
|
+
- A distro that dies with a request in flight spends the resident's one rebuild;
|
|
49
|
+
still down, the resident stays out until the app restarts.
|
|
120
50
|
|
|
121
51
|
## The GUI workspace-files service
|
|
122
52
|
|
|
123
|
-
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
(
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
round trips instead of one, and a deep page read streams the preceding lines
|
|
133
|
-
through the pipe before cutting. Expect the GUI file tree and previews to be
|
|
134
|
-
slower than a direct agent call, especially on large files; the model's own
|
|
135
|
-
file operations are unaffected — they use the preset filesystem and the
|
|
136
|
-
resident agent directly. Measured 2026-10-19 with
|
|
137
|
-
[`test/probe/gui-preview.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/gui-preview.sh) (20 iterations on
|
|
138
|
-
this machine): the routed backend's `stat` p95 2.7 ms, `listDir` p95 5.1 ms,
|
|
139
|
-
and a 1 MiB `readByteRange` p95 **63.5 ms/MiB** — the service's own round
|
|
140
|
-
trips sit on top of those, and the 1 s/MiB budget has a wide margin. No
|
|
141
|
-
optimization is justified by these numbers; revisit only with a measured
|
|
142
|
-
regression.
|
|
143
|
-
|
|
144
|
-
- **`fs-routing` is the seam.** The shipped service depends on the routed root
|
|
145
|
-
filesystem staying enabled and faithful to the `ctx.fs` contract (`resolve`,
|
|
146
|
-
`lstat`, `stat`, `listDir`, `streamText`, `readBytes`, `watch`, `contains`,
|
|
147
|
-
`processPath`). Disabling `fs-routing` restores the host 9p-backed root
|
|
148
|
-
filesystem, and the GUI file service then crosses 9p for WSL workspaces. The
|
|
149
|
-
shipped row's own caps (`maxBytes`, `maxFileBytes`, `maxLines`, `maxEntries`)
|
|
150
|
-
can be overridden on the `workspace-files` row.
|
|
53
|
+
- The GUI file tree and previews use the shipped `workspace-files` row, reading WSL
|
|
54
|
+
paths through the routed root filesystem — deliberately not replaced (a server-only
|
|
55
|
+
takeover would drop the browser `file` provider the Sidebar previews need).
|
|
56
|
+
- **Expect extra round trips** (roughly 4-6 per listing/stat/page; measured p95:
|
|
57
|
+
`stat` 2.7 ms, `listDir` 5.1 ms, 1 MiB read 63.5 ms/MiB — 2026-10-19,
|
|
58
|
+
[probe](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/gui-preview.sh)).
|
|
59
|
+
The model's own file operations are unaffected.
|
|
60
|
+
- `fs-routing` is the seam: disabling it restores the host 9p-backed root filesystem.
|
|
61
|
+
The shipped row's caps can be overridden on the `workspace-files` row.
|
|
151
62
|
|
|
152
63
|
## `@` completion
|
|
153
64
|
|
|
154
|
-
- **`@` references
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
median over 9p versus **1.5 ms** on ext4 (~56×;
|
|
159
|
-
[`probe:file-reference`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/file-reference-9p.sh), 2026-10-19).
|
|
160
|
-
[`src/file-reference-wsl.ts`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/src/file-reference-wsl.ts) is the distro-side
|
|
161
|
-
traversal (built and unit-tested) but is **not** in `cordis.patch.yml`: the
|
|
162
|
-
funnels it would replace are module-internal. The fallback is the 9p walk
|
|
163
|
-
until upstream exposes `FileReferenceTraversal`. Cost, seam and wire
|
|
164
|
-
trigger: [UPSTREAM-FILE-REFERENCE-SEAM.md](UPSTREAM-FILE-REFERENCE-SEAM.md).
|
|
65
|
+
- **`@` references walk the 9p share** (~56× slower than ext4 on a probed tree,
|
|
66
|
+
2026-10-19). The distro-side traversal is built but not mounted — it needs the
|
|
67
|
+
upstream `FileReferenceTraversal` seam:
|
|
68
|
+
[UPSTREAM-FILE-REFERENCE-SEAM.md](UPSTREAM-FILE-REFERENCE-SEAM.md).
|
|
165
69
|
|
|
166
70
|
## The sandbox
|
|
167
71
|
|
|
168
|
-
- **The one-shot fallback has only the host deadline
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
agent
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
hole: the confined profile shadows `/mnt` with an empty tmpfs, so files and
|
|
205
|
-
executables at paths under `/mnt` are hidden. Other mounts, alternate access
|
|
206
|
-
paths and unrestricted file reads are not covered by this mask. It is not a
|
|
207
|
-
closure — a command that can write the workspace can write an executable there and
|
|
208
|
-
run it (binfmt interop dispatches on file content, not location) — so enforcement
|
|
209
|
-
stays `partial`, the result's sandbox fact reports `windowsDrive: "masked"`, and
|
|
210
|
-
distro-level disabling of WSL interop (`[interop] enabled=false` in
|
|
211
|
-
`wsl.conf`) must be configured separately. On `wsl-shell`, an explicitly set
|
|
212
|
-
`interopVisibility` takes precedence over `maskWindowsDrive`.
|
|
213
|
-
- **`bubblewrap` must be installed**, or both providers fail immediately. The
|
|
214
|
-
usability probe re-runs after a failure, so installing bubblewrap while the app
|
|
215
|
-
runs is believed on the next command; only a success is cached. The refusal
|
|
216
|
-
classifies the probe's failure — missing versus present-but-failing — and the
|
|
217
|
-
direct install command follows the distro's package family; a probe failure
|
|
218
|
-
with no recognisable shape falls back to the install remedy with the raw
|
|
219
|
-
output quoted. A session-start preflight (`wsl-preflight`) surfaces the same
|
|
220
|
-
text as a warning; it is advisory and never installs.
|
|
221
|
-
- `workspace-write` binds the workspace root as writable, and bubblewrap refuses a
|
|
222
|
-
bind whose source directory does not exist, so a session whose workspace directory was
|
|
223
|
-
deleted fails with a runner error; the plugin does not recreate it. A workdir that
|
|
224
|
-
does not exist — a configured `cwd` pointing at a deleted directory, for instance — is
|
|
225
|
-
a separate case and is now reported as an error naming that directory, because
|
|
226
|
-
`wsl.exe` would otherwise run the command in `/` and exit 0.
|
|
72
|
+
- **The one-shot fallback has only the host deadline** — a killed `wsl.exe` does not
|
|
73
|
+
relay the kill into the distro, so the Linux process tree can outlive the call.
|
|
74
|
+
Prefer `agent: true` and the session runtime for long commands.
|
|
75
|
+
- **Batch reads share one native agent per distro** (16 concurrent calls in
|
|
76
|
+
2.4-4.1 ms; first call pays ~70 ms preparation).
|
|
77
|
+
- **aarch64 runs under qemu in CI only** (no Windows-on-ARM machine in reach —
|
|
78
|
+
[COMPATIBILITY.md](COMPATIBILITY.md#scope-decisions)). `auto` still selects the
|
|
79
|
+
packaged binary; `native` refuses on preparation failure; `shell` forces the shell
|
|
80
|
+
agent.
|
|
81
|
+
- **The session runtime needs systemd and a lingering user manager** — refused on the
|
|
82
|
+
first command with the remedy otherwise; `sessionSandbox: false` keeps the old
|
|
83
|
+
per-command shape. A hard app crash can leave a stale `dsh-wsl-*.scope`; the
|
|
84
|
+
preflight sweeps stale scopes on the next WSL session (never another live
|
|
85
|
+
session's).
|
|
86
|
+
- **The sandbox does not govern WSL interop** — reported as `enforcement: partial`.
|
|
87
|
+
`maskWindowsDrive` narrows `/mnt` but is not a closure; distro-level
|
|
88
|
+
`[interop] enabled=false` in `wsl.conf` must be configured separately.
|
|
89
|
+
[ARCHITECTURE.md](ARCHITECTURE.md#reported-enforcement).
|
|
90
|
+
- **`bubblewrap` must be installed**, or both providers fail with the install
|
|
91
|
+
command; a session-start preflight surfaces the same text as an advisory warning.
|
|
92
|
+
- A workspace directory deleted mid-session fails with a runner error; the plugin
|
|
93
|
+
does not recreate it. A configured `cwd` that does not exist is reported as an
|
|
94
|
+
error naming the directory.
|
|
95
|
+
|
|
96
|
+
## Shell startup semantics (`wsl-shell`)
|
|
97
|
+
|
|
98
|
+
Commands run as `<shell> -lc <command>` — login (profile scripts sourced, so version
|
|
99
|
+
managers work) and **non-interactive** (no prompt/plugin output in the model's
|
|
100
|
+
streams).
|
|
101
|
+
|
|
102
|
+
- **`~/.zshrc` / `~/.bashrc` are never read.** Move machine-level PATH setup
|
|
103
|
+
(`fnm`, nvm, cargo) into `~/.zprofile` or `~/.zshenv`; keep prompts and completion
|
|
104
|
+
in the rc file.
|
|
105
|
+
- **fnm's multishell PATH cannot form inside the sandbox** (`/run` is read-only) —
|
|
106
|
+
use the fallback recipe in
|
|
107
|
+
[Configuration](CONFIGURATION.md#zshfnm-recipe-for-model-run-commands).
|
|
227
108
|
|
|
228
109
|
## Agent diagnostics
|
|
229
110
|
|
|
230
|
-
- **The doctor's `agent.handshake`
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
- **`dsh-wsl-*.scope` counts are point-in-time.** The doctor lists and inspects
|
|
238
|
-
the user scopes once; a scope created or stopped a moment later is not
|
|
239
|
-
reflected. `--fix` runs the same sweep the session preflight uses.
|
|
240
|
-
|
|
241
|
-
## The doctor's sessions table
|
|
242
|
-
|
|
243
|
-
- **The sessions table shows distro-side facts only.** The cross-distro view
|
|
244
|
-
(`distro → scope → state → resident pids`) attests what the distro can see:
|
|
245
|
-
which scopes exist and which pids inside them are agent processes. A
|
|
246
|
-
resident's idle countdown and its `lastError` live in the host process that
|
|
247
|
-
owns it — there is no IPC channel from a plugin script to the app, so the
|
|
248
|
-
table cannot show them (`WSL-313`'s upstream ask). The agent.handshake
|
|
249
|
-
check notes a live resident's presence when the scope survey sees one, and
|
|
250
|
-
says that its own probe starts a fresh one.
|
|
111
|
+
- **The doctor's `agent.handshake` probes a fresh resident**, not the live one —
|
|
112
|
+
a running resident's `lastError` stays in the app log (`WslAgent.status()` exposes
|
|
113
|
+
it in-process).
|
|
114
|
+
- **`dsh-wsl-*.scope` counts are point-in-time**; `--fix` runs the preflight's sweep.
|
|
115
|
+
- **The doctor's `git.win-safe` probe reports the Windows git trust through interop
|
|
116
|
+
`git.exe`** — the `safe.directory` whitelist that WSL change review needs when
|
|
117
|
+
Windows git is installed (see Terminals below).
|
|
251
118
|
|
|
252
119
|
## Session-start advisories
|
|
253
120
|
|
|
254
|
-
- **There is no session-UI notice seam in DSH 0.2.1.**
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
anything, never cancels the session and never changes the fail-closed
|
|
258
|
-
behavior of the first command. The finding goes to the host log
|
|
259
|
-
(`logger.warn`) and into the session's runtime context as a `wsl-preflight`
|
|
260
|
-
entry — model-visible on the next assembly — but the GUI today has no
|
|
261
|
-
toast/banner service a plugin can publish to. `pnpm run doctor` is the
|
|
262
|
-
deterministic human-facing check; the exact upstream ask is
|
|
121
|
+
- **There is no session-UI notice seam in DSH 0.2.1.** `wsl-preflight` failures go
|
|
122
|
+
to the host log and the session's runtime context (model-visible next assembly) —
|
|
123
|
+
advisory only, never installing or cancelling. The upstream ask:
|
|
263
124
|
[UPSTREAM-SESSION-NOTICE.md](UPSTREAM-SESSION-NOTICE.md).
|
|
264
125
|
- **The runtime-context entry is a repeated snapshot, not a dismissible
|
|
265
|
-
notification.**
|
|
266
|
-
it has no severity decoration and no dismiss control. Removing the
|
|
267
|
-
`wsl-preflight` row (or setting `sandbox: false` on it) silences the probe
|
|
268
|
-
entirely.
|
|
126
|
+
notification.** Removing the `wsl-preflight` row silences the probe entirely.
|
|
269
127
|
|
|
270
128
|
## Ports
|
|
271
129
|
|
|
272
|
-
- **The port poller keeps the resident agent permanently warm
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
while a distro's session is active the agent never idles out: one `wsl.exe`
|
|
276
|
-
and one in-distro agent process stay resident per active distro even with no
|
|
277
|
-
command in flight. The poller starts only for a distro a session actually
|
|
278
|
-
uses; raising `portsRefreshMs` past the agent's idle timeout restores idle
|
|
279
|
-
shutdown, at the cost of staler port facts.
|
|
130
|
+
- **The port poller keeps the resident agent permanently warm** per active distro
|
|
131
|
+
(refresh 10 s). Raising `portsRefreshMs` past the agent's idle timeout restores
|
|
132
|
+
idle shutdown, at the cost of staler port facts.
|
|
280
133
|
|
|
281
134
|
## The directory picker
|
|
282
135
|
|
|
283
|
-
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
walk the host used to pay per entry. Entries arrive NUL-separated, so a
|
|
287
|
-
directory name containing a newline lists as one exact row (WSL-306 retired
|
|
288
|
-
the `ls -1ALp` line parse that used to split such a name into two rows or
|
|
289
|
-
drop it).
|
|
136
|
+
- Distro levels list from inside the distro in one round trip (NUL-separated, so
|
|
137
|
+
newline-bearing directory names list exactly — WSL-306); a one-shot `wsl.exe --exec
|
|
138
|
+
find` when the resident is out.
|
|
290
139
|
|
|
291
140
|
## Terminals
|
|
292
141
|
|
|
293
|
-
- Terminal routing is per-session by workspace coordinates
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
`
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
lose that shell), so does any process the shell's startup files spawn with
|
|
316
|
-
the inherited marker (an `ssh-agent` launched in `.bashrc` reads as busy);
|
|
317
|
-
the idle threshold is the controller's `unattendedTimeoutMs` (2 h by
|
|
318
|
-
default); and the host-side shell-activity integration is still not armed
|
|
319
|
-
for these launches — `wsl.exe` on win32 fails its gate — which is why the
|
|
320
|
-
observation is a scan rather than shell cooperation. `terminalIdleReclaim:
|
|
321
|
-
false` on the `subprocess-wsl` row restores close-by-hand: the launch then
|
|
322
|
-
carries no `DSH_TERMINAL_ID` marker, and the handle keeps the shipped
|
|
323
|
-
`unknown` activity, which never accumulates idle.
|
|
324
|
-
- Windows-folder host terminals (the `powershell.exe` branch) keep the shipped
|
|
325
|
-
behaviour: `unknown` activity, no reclamation.
|
|
142
|
+
- Terminal routing is per-session by workspace coordinates: a WSL-folder session gets
|
|
143
|
+
the distro shell, a Windows-folder session gets `powershell.exe` in its own
|
|
144
|
+
directory. The shell MENU's default entry stays the configured profile; a
|
|
145
|
+
directory-less launch lands in the distro.
|
|
146
|
+
- **Windows git must trust the share** (`safe.directory`), or WSL workspaces'
|
|
147
|
+
changed-files cards silently disappear (the discovery call answers
|
|
148
|
+
`detected dubious ownership`). One-time remedy on Windows:
|
|
149
|
+
`git config --global --add safe.directory "//wsl.localhost/*"`, then restart the
|
|
150
|
+
app. Two alternatives: the doctor's `git.win-safe` probe reports the state, and a
|
|
151
|
+
machine **without** Windows git needs nothing — the provider routes discovery into
|
|
152
|
+
the distro and translates the output into UNC form.
|
|
153
|
+
- **A NEW tab's title reads `WSL`** whatever it runs — the controller titles from the
|
|
154
|
+
composition's profile before the provider rewrites the launch, and a plugin cannot
|
|
155
|
+
correct the default ([UPSTREAM-TERMINAL-TITLE.md](UPSTREAM-TERMINAL-TITLE.md)).
|
|
156
|
+
The menu ships host candidates (`powershell`, `cmd`): a manually selected shell is
|
|
157
|
+
titled after itself; double-click rename also works.
|
|
158
|
+
- **Distro terminals ARE reclaimed when idle** — the handle counts the marked
|
|
159
|
+
processes through the resident agent (exactly one at its prompt = `idle`; a nested
|
|
160
|
+
shell or an `ssh-agent` from startup files reads `busy` by design). Threshold: the
|
|
161
|
+
controller's `unattendedTimeoutMs` (2 h). `terminalIdleReclaim: false` restores
|
|
162
|
+
close-by-hand. Windows-folder host terminals keep the shipped no-reclamation
|
|
163
|
+
behaviour.
|
|
326
164
|
|
|
327
165
|
## Host "Open in app"
|
|
328
166
|
|
|
329
|
-
- **Opening VS Code
|
|
330
|
-
|
|
331
|
-
`
|
|
332
|
-
`Code.exe` starts as Node, exits 1, and the UI reports launch-failed. File
|
|
333
|
-
Explorer and other non-Electron launchers are unaffected; the failure is the
|
|
334
|
-
same on a Windows folder and on a WSL UNC workspace. This repository does
|
|
335
|
-
not own that spawn path. The record and the upstream fix are in
|
|
167
|
+
- **Opening VS Code from "Open in app" can fail independently of this plugin** — the
|
|
168
|
+
Host runs with `ELECTRON_RUN_AS_NODE=1`, which the launcher inherits, so
|
|
169
|
+
`Code.exe` starts as Node and exits 1. Not owned by this repository:
|
|
336
170
|
[UPSTREAM-OPEN-IN-APP.md](UPSTREAM-OPEN-IN-APP.md).
|