dsh-win-pwsh7 0.0.0-stage → 0.2.3

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 ADDED
@@ -0,0 +1,426 @@
1
+ # Changelog
2
+
3
+ ## 0.2.3 — removable dsh-ops coexistence bridge
4
+
5
+ - Moved the bridge, profile-patch ownership, and pure name planning into `lib/dsh-ops-bridge.js`.
6
+ `index.js` now mounts it in one place; `package.json` ships the module.
7
+ - Enabled coexistence declaratively from `cordis.patch.yml`; the plugin no longer writes an
8
+ enable override into the profile. Turning the switch off writes only a marked disable override.
9
+ - Removal contract: delete the module, the mount line, the config field, the client switch, and its
10
+ gate; the bundle patch disappears with the package. A switch-off override is the only possible
11
+ profile residue and is removed by the plugin's cleanup when it runs.
12
+ - The bundle patch must come after `dsh-ops` in `dsh.profile.bundles`; otherwise the loader warns
13
+ and skips it. The profile keeps `dsh-ops` before `dsh-win-pwsh7`.
14
+
15
+ ## 0.2.2 — fix the dsh-ops bridge freeze
16
+
17
+ - Fixed the 0.2.1 hide/unhide oscillation that could pin the Host main thread when many sessions
18
+ were restored. The bridge now plans from the host's **pre-restriction** `knownNames` /
19
+ `restrictableNames` instead of the post-restriction `visible` set, and filters the deny list to
20
+ names `tools.restrict()` actually accepts. The fallback path latches names it already hid.
21
+ - Added regression checks for the stable plan, the older-host fallback, and the scope-local
22
+ `ops_bash` case that used to throw an unknown-global-tool warning.
23
+ - Restored `dshOpsCompat` to default-on after multi-session validation of the fixed bridge.
24
+ - The configuration page now uses the native `Switch` primitive and native setting-row layout for
25
+ the coexistence toggle; labels and hints no longer expose raw config keys.
26
+
27
+ ## 0.2.1 — dsh-ops coexistence bridge
28
+
29
+ - Added a default-on `dshOpsCompat` configuration row. When dsh-ops is installed, pwsh7 writes
30
+ a marked `dsh-ops-compat` override into the active profile patch and enables it, so the native
31
+ `pwsh` tool is visible again alongside `ops_bash`.
32
+ - The agent bridge re-hides the official `read` / `glob` / `grep` tools when their OPS
33
+ counterparts are visible, plus the duplicate official `bash` / `pwsh_persistent` shell. It
34
+ keeps `pwsh` and `ops_bash` and does nothing when dsh-ops is absent.
35
+ - The compatibility patch needs a DSH restart; turning the switch off removes only the block this
36
+ plugin wrote. Uninstall cleanup is best-effort and leaves at most a harmless compatibility row.
37
+ - Added the plugin-page toggle with its own localized row and hint.
38
+
39
+ ## 0.2.0 — 优化 Store 版 pwsh7 安装方式
40
+
41
+ 优化store版pwsh7安装方式,重构项目前端设计和工程设计,使其符合原生DSH工程习惯和界面风格。
42
+
43
+ - Removed the retired internal `method: 'store'` / winget install route. `pwsh7_install` has only
44
+ the Store-page script path plus the manual path; the tool schema never exposed the winget route.
45
+ - Removed the winget-only job spec, log follower, download measurement, failure-code translation,
46
+ inline fallback, constants, imports, and their test fixtures. The host gate now drives only the
47
+ Store-UI job, the VPN confirmation, the breaker, the disabled-row report, and activation.
48
+ - Deleted `docs/acceptance-0.13.0.md`, which described the retired metered/winget behaviour.
49
+ - READMEs no longer mention `installIfMissing`, the winget installer, or a "configuration card";
50
+ they describe the current two install paths and the status card.
51
+ - Declared `engines.dsh` (`>=0.2.1-rc.3 <0.2.2-0`) so an untested DSH line is rejected at install.
52
+
53
+ ## 0.16.6 — status card matching the official provider card
54
+
55
+ - The status section is now a native bordered card with an inner estimate/facts surface, matching
56
+ the official speech-to-text provider card: card title, three facts (status, runtime, executor),
57
+ footnote, and the compact Re-detect action.
58
+ - The Remote `status` endpoint now also returns the Host gate snapshot, so the card renders facts
59
+ instead of the model-facing report. The client falls back to the report when an older Host
60
+ answers without it.
61
+ - When the gate is present the raw report is no longer shown; the pasteable wiring patch remains
62
+ available in the card.
63
+
64
+ ## 0.16.5 — native plugin detail page
65
+
66
+ - The plugin page's two sections now match the native detail page instead of drawing their own
67
+ cards: flat sections, 14px/500 section titles, the page's 32px section rhythm, 34px native inputs,
68
+ 28px outline actions for Re-detect and Copy, and the native settings-form Save/Reset with matching
69
+ focus rings.
70
+ - The status report is native body text rather than a scrollable log box; the pasteable patch keeps
71
+ a compact native code block and copy action.
72
+ - Removed the stale metered-network warning and its dictionary entries — the feature was already
73
+ gone, but the client still carried its copy.
74
+ - The plugin card/detail description now describes the current behaviour (script or manual Store
75
+ install; no metered preflight).
76
+
77
+ ## 0.16.4 — make the companion recommendation an instruction to the model
78
+
79
+ - The completed script-install verdict now begins the companion text with `ACTION REQUIRED:`: the
80
+ model must tell the user that dsh-win-pwsh7 is best paired with the dsh-ops plugin, offer to install
81
+ it, and not install it without consent. The repository link and install command stay included.
82
+ - READMEs describe it as an instruction, not a passive recommendation.
83
+ - It is still emitted only in the completed script-install result.
84
+
85
+ ## 0.16.3 — link the dsh-ops companion
86
+
87
+ - The completed script-install recommendation now includes the companion repository link:
88
+ https://github.com/T-Auto/dsh-ops.
89
+ - Both READMEs link the same repository.
90
+
91
+ ## 0.16.2 — recommend the companion only after the script install completes
92
+
93
+ - 0.16.1 appended the dsh-ops recommendation to every healthy `pwsh7_status` report. That was too
94
+ broad: the request is a **one-time recommendation to the model after the script install
95
+ completes**.
96
+ - The recommendation now appears only in the successful Store-UI job verdict, after the runtime is
97
+ detected. `pwsh7_status`, manual installs, and the plugin page do not advertise it.
98
+ - READMEs and the host gate assert the narrower behavior.
99
+
100
+ ## 0.16.1 — recommend dsh-ops after a working runtime appears
101
+
102
+ - `pwsh7_status` appends a companion recommendation once a usable PowerShell 7 is detected, and a
103
+ successful Store-UI install includes it in the job verdict: **dsh-ops** — native file/PTC tools
104
+ plus Windows host PowerShell integration — pairs well with this plugin. It is explicitly not
105
+ installed automatically; the suggested command is
106
+ `dsh plugin --profile <profile> add dsh-ops`.
107
+ - Both READMEs document the recommendation.
108
+ - Host gate asserts a healthy status report names `dsh-ops` and says it is not installed
109
+ automatically.
110
+
111
+ ## 0.16.0 — no startup auto-install, three-failure breaker
112
+
113
+ - **`installIfMissing` is gone.** The config field, its activation branch, the install-attempt report
114
+ text and the client switch are removed. Starting the Host only detects and reports; installing is
115
+ always an explicit `pwsh7_install` (or the manual Win+R path). This also removes the old winget
116
+ startup route, so the only user-facing install methods are script install and manual install.
117
+ - **Three-failure breaker, per Host process.** Consecutive failed script installs are counted
118
+ (exit 2/3/4/5 and job/spawn errors; a started-but-not-yet-installed click does not count). After
119
+ 3, `pwsh7_install` starts no job: it asks the user to check the network/VPN, and only accepts
120
+ `userConfirmedFixed: true` after the user explicitly says it is fixed. That resets the counter and
121
+ allows one retry; three more failures open the breaker again. Success and an already-installed
122
+ runtime reset it. The failure text now begins by asking whether a VPN is active.
123
+ - **Tool schema** gains `userConfirmedFixed`; the block is enforced before the VPN confirmation, so
124
+ repeating `vpnOffConfirmed: true` cannot bypass it.
125
+ - **Docs** rewritten to the current behavior: two install paths, VPN confirmation, Store notice,
126
+ breaker, no metered preflight, three gates, no `installIfMissing`.
127
+ - **Tests.** Host gate covers the breaker/no-job path and the explicit-fix reset; activation must
128
+ not start an install; client gate no longer expects the removed switch; all old config-driven
129
+ install tests use `method: 'store'` internally.
130
+
131
+ ## 0.15.1 — the Store script survives 5.1 encoding, and only two install paths
132
+
133
+ The 0.15.0 Store-UI script failed before opening the page with a parser error, not a VPN problem:
134
+ Windows PowerShell 5.1 decoded the inline text in the host's ANSI code page, and characters inside
135
+ the Chinese notice string became illegal tokens.
136
+
137
+ - **The script is a UTF-8 BOM file, not inline text.** `spawnStoreUi` writes it to a temporary
138
+ `.ps1` with a BOM and launches `powershell.exe -File`; 5.1 reads BOM-prefixed files as UTF-8
139
+ regardless of the console code page. The temporary directory is removed when the job settles.
140
+ - **Smart quotes are gone.** The Store notice uses 「安装」/「取消」 instead of curly quotes, which
141
+ PowerShell 7 itself treats as string delimiters. The generated script now parses under both
142
+ Windows PowerShell 5.1 and PowerShell 7.
143
+ - **Only two install paths are exposed.** The `pwsh7_install` schema no longer has a `method`
144
+ parameter and the status/report text no longer offers a winget command: the tool is the script
145
+ install (open Store, notice, click Install), and the other path is manual Win+R. The manual
146
+ instructions remain in every report and failure message.
147
+ - **Tests.** The host gate still drives the Store-UI click/failure jobs and no longer expects a
148
+ winget command in the report.
149
+
150
+ ## 0.15.0 — VPN confirmation, Store notice, no metered probe, no context injection
151
+
152
+ The 2026-10-27 run exposed three usability gaps around the Store-UI install and one unnecessary
153
+ mechanism: the model could start an install without asking about the VPN; the Store window could be
154
+ closed because nothing explained it; the metered preflight was no longer relevant to the manual
155
+ Store page; and the injected status note was unwanted.
156
+
157
+ - **VPN is now a question, not a probe.** `pwsh7_install` requires `vpnOffConfirmed: true`. Without
158
+ it the tool starts nothing and returns: ask the user whether a VPN is active, have them
159
+ disconnect it and restart Microsoft Store, then call again. There is no adapter/default-route
160
+ detection anywhere.
161
+ - **The Store window explains itself.** Before opening the page, the UI script shows a modal
162
+ `MessageBox`: dsh-win-pwsh7 is about to open Microsoft Store and click Install; do not close the
163
+ Store window; if a VPN is active, cancel, disconnect it, fully quit/restart Store, then retry.
164
+ Closing the notice with Cancel exits cleanly and the job reports "cancelled at the Store notice".
165
+ - **Metered/network preflight removed.** `METERED_COST`, `NETWORK_COST_SCRIPT`, `networkCost`,
166
+ `networkWarning`, the metered status warning and the install preflight are gone. `store-ui` is
167
+ the manual Store page, where the metered queue behaviour does not apply; `method: 'store'` keeps
168
+ the winget job but no longer probes the connection.
169
+ - **Context injection removed.** `agent.inject`, `createUserMessage`, the settled-state note and the
170
+ related contract check are gone; the job output and `pwsh7_status` remain the sources of truth.
171
+ - **Tests.** The host gate asserts the confirmation gate starts no job, a store-ui click job, a
172
+ page-not-loaded failure with VPN/manual guidance, and no network preflight; the probe environment
173
+ stays isolated from the host's real PowerShell.
174
+
175
+ ## 0.14.0 — open the Store page and click Install
176
+
177
+ The 2026-10-27 test proved that the manual Store page is the path that works, and that a script can
178
+ open it and invoke the same Install button with Windows UI Automation. `pwsh7_install` now defaults
179
+ to that route; winget remains available as `method: 'store'`.
180
+
181
+ - **`store-ui` (default).** It opens
182
+ `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`, waits up to 90 seconds for the Store window, then
183
+ invokes the localized Install / 获取 / Get / 更新 / Update button through `UIAutomationClient` and
184
+ falls back to `LegacyIAccessiblePattern`. This is the same Store page and button a human uses.
185
+ - **A click is not a failed install.** The job reports "Store page opened and Install was clicked —
186
+ downloading/installing in the background", tells the model not to retry, and asks it to poll
187
+ `pwsh7_status`; a runtime found within the bounded re-check is reported as installed.
188
+ - **Page-load failure asks about the VPN.** If no Install button appears within the timeout — or the
189
+ page cannot be opened/invoked — the report says the Store page did not load, and tells the user:
190
+ disconnect any VPN, fully quit and restart Microsoft Store, then retry; manual Win+R steps remain.
191
+ - **VPN auto-detection is gone.** The 0.13.4 adapter/default-route heuristic was removed; the
192
+ network probe is back to `<cost>|<profile>` and the only automatic connection gate is metering.
193
+ The VPN question is asked only when the Store page fails to load, as the user requested.
194
+ - **`store` remains.** `pwsh7_install` with `method: 'store'` runs the existing winget background job
195
+ with its queue hints and 30-second polling guidance.
196
+ - **Tests.** The host gate drives a successful Store-UI click, a page-not-loaded failure with VPN
197
+ and manual guidance, and keeps all winget tests on `method: 'store'`; the probe environment is
198
+ isolated from the host's real PowerShell so the gate is reproducible with PowerShell 7 installed.
199
+
200
+ ## 0.13.4 — configure-it guidance, manual Store steps, VPN preflight
201
+
202
+ Two gaps from the 2026-10-27 run: the blocked `pwsh` call mentioned install and disable but not the
203
+ case where PowerShell 7 is already installed and only needs wiring, and the automatic install was
204
+ also blocked by an active VPN while the plugin only understood metering.
205
+
206
+ - **The denial and status now offer three ways.** When a usable PowerShell 7 is detectable, the
207
+ first answer is "configure the executor": call `pwsh7_status` for the exact
208
+ `pwsh-sandbox.pwshPath` patch, apply it to the active profile and reload DSH. Installing
209
+ (`pwsh7_install` or manual) and disabling stay available. The status report says
210
+ "Three ways forward" with the same split.
211
+ - **Manual Store install is spelled out.** Every install path prints:
212
+ press Win+R, paste `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`, press Enter; outside a VPN the
213
+ Store page for PowerShell 7 opens — click Install. A manual install is auto-detected; no pin.
214
+ (The product id in the text is generated from `STORE_PRODUCT_ID`.)
215
+ - **VPN is detected and intercepted.** The Windows PowerShell 5.1 probe now also inspects active
216
+ adapters whose name/description match common VPN/TUN clients, a default route through such an
217
+ adapter, and `Get-VpnConnection`. A reading of `Unrestricted|<profile>|WireGuard` (or any active
218
+ VPN) makes the install preflight refuse to start and returns: disconnect the VPN, fully quit and
219
+ restart Microsoft Store, retry; or use the manual Store steps. `pwsh7_status` shows the same
220
+ warning; a failed install re-checks and names the VPN if it appeared after the preflight.
221
+ The adapter-name match is a heuristic — a false positive still leaves the manual Store install.
222
+ - **Tests.** The host gate asserts manual/configure text in the denial, a VPN preflight that starts
223
+ no job and names the adapter, and the VPN status warning.
224
+
225
+ ## 0.13.3 — queued Store, VPN, 30-second polling, context injection
226
+
227
+ The automatic Store download can sit in "Queued" indefinitely; the reports confirmed the cause is
228
+ the Store client/queue, not the plugin's preflight. This release makes that state actionable and
229
+ stops the model from waiting on one long `job_output` call.
230
+
231
+ - **The queue is a first-class recovery path.** The install start report, the 2-minute progress
232
+ hint, and the failure verdict all say: pause/resume the PowerShell entry in Microsoft Store →
233
+ Downloads/Library; if the Store itself is stuck, disable any VPN, fully quit and restart Microsoft
234
+ Store; or install manually from `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`. A manual Store
235
+ install is detected automatically, no `pwshPath` pin.
236
+ - **The model is told to poll in 30-second rounds.** `pwsh7_install` now says
237
+ `job_output(wait:true, timeout_ms:30000)` then `job_list`; a `wait aborted` is not an install
238
+ failure. This is guidance for the model, not a change to the job's own 15-second progress tick.
239
+ - **The settled state is injected into the agent context.** When the background job finishes, the
240
+ owning agent receives a short `agent.inject()` note (feature-detected): runtime now effective,
241
+ install failed with the queue/VPN/manual options, or installed-but-still-pinned. The prompt
242
+ section alone could not tell a model that stopped looking.
243
+ - **The queue hint names VPN.** The progress line after `Started MSStore package execution` now
244
+ includes pause/resume, VPN off + restart Store, and the Windows Update pause check.
245
+ - **Tests.** The install start report carries the 30-second/`timeout_ms:30000`/VPN text; the
246
+ settled failure injects a note into a stub agent.
247
+
248
+ ## 0.13.2 — the Store handoff is named correctly
249
+
250
+ The 2026-10-27 work-exp reports showed the automatic install failing on a **non-metered**
251
+ connection while the plugin still told the user to turn metering off. The failure path now re-reads
252
+ the connection, translates the Store error codes, and offers the recovery that actually worked:
253
+ the Store entry was parked in "Queued", and pause → resume started the download.
254
+
255
+ - **Failure attribution re-reads the connection.** `installVerdict` calls `networkCost` again;
256
+ metered advice appears only for `Fixed` / `Variable`. An unreadable cost is explicitly not
257
+ attributed to metering.
258
+ - **Store error codes are translated.** `wingetFailureCode` normalizes decimal exit codes to
259
+ `0x…`, and `describeWingetFailure` explains `0x80D03801` (download/deployment), `0x80004004`
260
+ (E_ABORT) and `0x80072EFD` (WinHTTP 12029).
261
+ - **Non-metered failures point at the queue and the manual fallback.** The verdict says to open
262
+ Microsoft Store → Downloads/Library, pause and resume a "Queued" PowerShell entry, check Windows
263
+ Update is not paused, and gives `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`. A manual Store
264
+ install is detected automatically — no `pwshPath` pin is needed.
265
+ - **The handoff is visible in progress.** `Started MSStore package execution` is now a named phase;
266
+ after `DSH_WIN_PWSH7_STORE_QUEUE_HINT_MS` (default 2 minutes) the progress line says the entry may
267
+ be queued, and the stall line names both the queue and metering instead of only metering.
268
+ - **The gate stub formats `%s`** (OBS-01), so the host gate no longer prints the placeholder.
269
+ - **Tests.** A non-metered failure must not give metering advice; `0x80D03801` must be translated and
270
+ keep the manual Store path; the metered preflight still starts no job.
271
+
272
+ ## 0.13.1 — install, then actually usable
273
+
274
+ The 0.13.0 gate read the executor correctly but inherited the executor's own cache: `pwsh7_install`
275
+ could install PowerShell 7.6.9 while the running `PwshLocalExecutor` still resolved to 5.1, so the
276
+ tool stayed blocked and `pwsh7_status` could even answer "Nothing to configure". Both are fixed.
277
+
278
+ - **The install now nudges the executor cache.** `PwshLocalExecutor.pwshPath` re-resolves only when
279
+ its declared config changes. After a successful install the plugin writes a sentinel into that
280
+ cache and re-reads the getter, so the same running process resolves the new Store alias. No
281
+ profile patch and no restart are needed when the concrete executor exposes the cache.
282
+ - **The nudge is feature-detected.** If a composition does not expose `declaredPwshPath` /
283
+ `resolvedPwshPath`, the install report says so and prints the pin + reload patch instead of
284
+ pretending the tool is ready. `tools/check-native-contracts.mjs` now guards both fields.
285
+ - **The status can no longer call a stale executor "native".** When the live executor still reports
286
+ 5.1, the report keeps the `running executor still resolves to Windows PowerShell 5.1` warning,
287
+ prints the pin patch, and never says "Nothing to configure" just because the standalone resolver
288
+ now finds a Store alias.
289
+ - **Failures that cannot be nudged are explicit.** `installVerdict` distinguishes "the running
290
+ executor now resolves to 7.6.9" from "it still resolves to 5.1; pin pwsh-sandbox.pwshPath and
291
+ reload DSH", so the user is not left with a contradictory success message.
292
+ - **The Re-detect button moved into the status section.** It used to sit in the detail page's
293
+ head actions, away from the text it refreshes; it now renders beside the status title, where a
294
+ reader looks for it. `plugins.detail.actions` no longer carries a second copy.
295
+ - **Tests.** A stale live 5.1 + resolver 7 case must not render "Nothing to configure"; a stubbed
296
+ executor cache must be refreshed by the nudge; the contract gate asserts the cache fields exist;
297
+ the client gate renders the section's own Re-detect and drives the Remote flow through it.
298
+
299
+ Measured against the 2026-10-27 work-exp report: install landed 7.6.9, the tool remained blocked
300
+ until a manual `pwsh-sandbox.pwshPath` pin, and status said "Nothing to configure" in the same
301
+ window. This release removes that manual step on the normal path and makes the fallback honest.
302
+
303
+ ## 0.13.0 — enabled means gated
304
+
305
+ The plugin was observational: with only Windows PowerShell 5.1 it warned the model and let the
306
+ command run. This release makes the enabled state a contract. The model sees a call-time error
307
+ before dispatch, the user sees the install/configure/disable choices, and no PowerShell process is
308
+ started for a denied call.
309
+
310
+ - **The gate reads the executor, not a reconstruction.** `ctx.shell.pwshPath` is the exact
311
+ executable `dsh-tool-pwsh` will spawn (`PwshLocalExecutor.argv()` uses it). The read
312
+ feature-detects that concrete getter and falls back to `shell.config.pwshPath.get()` +
313
+ `resolvePwshPath()`, then to the default resolver. `configEditor.entries()` only exposes
314
+ `include`-owned rows, so a Desktop-generated live row could be missed; the report still lists the
315
+ rows for diagnosis.
316
+ - **`ctx.tools.guard` denies `pwsh` before dispatch.** A per-context snapshot drives the guard; a
317
+ `tools/pre-execute` listener refreshes it when the snapshot is unknown or older than
318
+ `DSH_WIN_PWSH7_GATE_TTL_MS`, and `app-boot/config-reload` / `plugin-manager/changed` refresh it
319
+ after profile changes. `pwsh7_status` and `pwsh7_install` are never gated.
320
+ - **The denial is self-contained.** It names the reason (5.1, broken pin, no PowerShell 7, unknown),
321
+ says not to retry, and offers `pwsh7_status` (exact patch), `pwsh7_install` (after the user
322
+ agrees), or disabling the plugin. The 5.1 prompt section now says the tool is blocked instead of
323
+ advising the `;` workaround.
324
+ - **Install preflight.** `resolveWinget()` then `networkCost()`: the install starts only for
325
+ `Unrestricted` with a readable profile. `Fixed` / `Variable` / `Unknown` / an unreadable probe /
326
+ a missing winget returns a report naming the network and the setting to turn off, and registers
327
+ **no job**. `installIfMissing` skips with the same reason instead of starting a doomed install.
328
+ - **Configuration guidance.** The report keeps the row's existing config fields (the `config` block
329
+ is replaced wholesale), distinguishes add vs update, and states that `overridePath` only changes
330
+ probing and recommendations — not executor wiring.
331
+ - **Client.** The diagnostics section derives a metered-network line and a fix block with the
332
+ profile patch and a copy button from the report it already receives.
333
+ - **Native contracts.** `docs/native-contracts.md` records the 0.2.1-rc.3 package table and the
334
+ symbols the plugin relies on; `tools/check-native-contracts.mjs` (36 checks over 12 packages)
335
+ runs after the host and client gates and fails when a symbol or prototype disappears.
336
+ - **Tests.** Gate allow/deny, broken pin, own tools, pre-execute refresh, config reload, add/update
337
+ patch, wholesale-config warning, overridePath-not-wiring, metered/unknown/no-winget starting no
338
+ job, and the client's network line and copyable patch.
339
+
340
+ This is a breaking behavioral change for anyone who relied on the 5.1 fallback while the plugin was
341
+ enabled. Disabling the plugin restores the previous behavior; the plugin still never writes
342
+ `pwshPath` and never uninstalls PowerShell.
343
+
344
+ ## 0.12.3 — the gates read the desktop build's nested harness
345
+
346
+ The desktop build (`DeepSeek Harness.exe` 0.2.1-rc.2) packs a different layout than 2.0.19-next: the
347
+ archive root carries only a shell — `dsh`, `node_modules`, `lib`, `package.json`, `renderer` — and its
348
+ `node_modules/@deepseek-ai` holds eight packages (cordis, cosmokit, dsh-api-gateway, dsh-brand,
349
+ dsh-deque, dsh-timeout, dsh-typert-protocol, schemastery), while all 306 harness packages sit under
350
+ `dsh/`. `0.12.2` accepted any root carrying `cordis`, so it picked the shell and the Host gate died
351
+ with `ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/dsh-tools'`.
352
+
353
+ - **The harness is recognised by carrying both `cordis` and `dsh-tools`**, not `cordis` alone, and a
354
+ root or its `dsh`/`app` subdirectory is searched for it — `harnessAt()` in
355
+ `tools/dsh-app-modules.mjs`.
356
+ - **The unpack now also takes `dsh/`**, so a packed desktop archive yields `<cache>\dsh`, and the
357
+ `note — the harness packages come from an asar archive, unpacked for this run into …` line names it.
358
+ - **A cache created by an older release is not trusted**: it is reused only when the full harness is
359
+ inside it, so upgrading the plugin repairs the stale unpack instead of failing again.
360
+ - **Measured**: new desktop install 30 s cold / 6 s cached, previous 2.0.19-next install 6 s cached,
361
+ both gates green; no installation at all still prints the actionable note and exits 1.
362
+ - **The READMEs now describe all three layouts** and the "Releasing" sections explain that the tag
363
+ publishes through CI (trusted publishing, OIDC) and that provenance requires `repository.url` to be
364
+ byte-identical to the GitHub context.
365
+
366
+ ## 0.12.2 — the gates read a packed DSH 2.0 install
367
+
368
+ DSH 2.0.19-next ships its packages inside `resources/app.asar`, and the Host gate could not find them
369
+ there: Node resolves nothing inside an archive, and the sibling `app.asar.unpacked` holds only five
370
+ packages (cordis is not among them), so the run ended in
371
+ `ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/cordis'`. The plugin's own halves were never
372
+ affected — both load and run on 2.0.19-next — but nobody could re-run the gates on a 2.0 machine,
373
+ which is exactly when they are wanted.
374
+
375
+ - **`tools/dsh-app-modules.mjs` (new)** resolves the harness: `DSH_APP_DIR` first, then the nearest
376
+ ancestor of the tools directory or the working directory that carries
377
+ `node_modules/@deepseek-ai/cordis`. `DSH_APP_DIR` now accepts an install root, `resources/app`
378
+ (an unpacked install), or `resources/app.asar` (a packed one).
379
+ - **A packed installation is unpacked once** into `%TEMP%\dsh-app-modules-<archive>-<size>-<mtime>` —
380
+ only `node_modules` and `package.json`, staged under `<cache>.<pid>` and renamed into place, reused
381
+ by later runs. Unpacking the real tree instead of re-implementing Node's resolution over the archive
382
+ is deliberate: a hand-written resolver can disagree with Node on `exports` maps, conditions, and
383
+ CJS/ESM, and that disagreement would show up as a green gate that proves nothing.
384
+ - **The hook and the Host gate use it**, and the gate says where the packages came from. A machine
385
+ with no installation at all still fails the gate — with one actionable line naming the three
386
+ accepted hand-offs, instead of a bare `ERR_MODULE_NOT_FOUND`.
387
+ - **Measured on 2.0.19-next**: 38 s cold (the unpack is most of it), 6 s on the cached second run,
388
+ both gates green.
389
+
390
+ ## 0.12.1 — first public release
391
+
392
+ `dsh-win-pwsh7` puts a real PowerShell 7 under the Harness `pwsh` tool on Windows. This is the first
393
+ release published outside its author's machine, and it is the behaviour the README documents:
394
+
395
+ - **Detection and reporting** — `pwsh7_status` reads the executor rows, validates an explicit path,
396
+ refuses a version-numbered Store path as un-pinnable, and prints a pasteable profile patch for the
397
+ version-stable Store family alias.
398
+ - **One install path** — `pwsh7_install` installs the Microsoft Store package as a background job.
399
+ It reads the connection cost first, because Windows defers Store downloads on a connection marked
400
+ as metered, and the remedy it offers is that setting — never a second installer. When PowerShell 7
401
+ is not wanted at all, the other answer is to disable the plugin.
402
+ - **A failed exit is evidence, not a verdict** — the runtime is re-probed before a failure is
403
+ reported, because the Store can finish an install after winget has already exited non-zero.
404
+ - **Progress that is measured** — the job follows winget's own diagnostic log for named phases
405
+ (downloading, deploying, registering) and measures the installer download from the bytes on disk,
406
+ because winget reports no numeric progress of its own.
407
+ - **A prompt section** — the model is told which PowerShell the executor actually runs, so it knows
408
+ whether `&&` and `||` work before it writes a command.
409
+ - **Two offline gates** under `tools/`, shipped with the package so anyone can re-run them. They run
410
+ unconditionally — the behaviour half is fixture-pinned (`PATH`, `LOCALAPPDATA`, `ProgramFiles`)
411
+ rather than skipped on a machine that has the per-machine MSI — they cover job cancellation and
412
+ both inline install fallbacks, and every assertion has been shown to fail under a deliberate
413
+ mutation.
414
+ - **Reviewed independently before release** — three Open Code Review passes, 36 findings triaged.
415
+ 30 were confirmed and fixed. The material ones: a gate that silently skipped half its checks; all
416
+ inline install paths taking a single-shot verdict instead of the bounded re-probe; a stall warning
417
+ that had become unreachable; a cancelled job that kept working (twice — the re-probe loop was the
418
+ second half); an activation-time install that reported nothing because it waited forever; a report
419
+ that could name a disabled row as the runtime. 4 findings were refuted with citations (the harness's
420
+ own schema dialect, the ecosystem's client descriptor shape, the optional `jobs` service, and the
421
+ string-error envelope), and 1 is recorded as backlog: the module-level state a second application
422
+ in one process would share.
423
+ - **Confirmed on the machine that found it** — the "a disabled row is never named as the runtime" fix
424
+ is not theoretical. This profile carries `pwsh-sandbox` (disabled) beside
425
+ `desktop-windows-pwsh-sandbox` (enabled), both pinning the same Store family alias; the report named
426
+ the *disabled* row until the fix, and after the restart it names the enabled one instead.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 XinSpark
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,171 @@
1
+ # dsh-win-pwsh7
2
+
3
+ <p align="center"><sub><a href="README.md">简体中文</a> · English</sub></p>
4
+
5
+ Give the Harness `pwsh` tool a real PowerShell 7 on Windows instead of silently falling back to
6
+ Windows PowerShell 5.1.
7
+
8
+ 1. **Detect** PowerShell 7 on the machine (plus an optional `overridePath`);
9
+ 2. **Validate** an explicit path (`pwsh7_status`'s `path` argument);
10
+ 3. **Install** via the script path (`pwsh7_install` opens the Microsoft Store page and clicks
11
+ Install) or the manual path (Win+R, same Store link). There is no automatic startup install.
12
+
13
+ While enabled, the `pwsh` tool is allowed **only when the live executor resolves to PowerShell 7+**.
14
+ Otherwise every call is denied by `ctx.tools.guard` before dispatch, the model is told to configure /
15
+ install / disable, and no PowerShell process starts. Disabling the plugin removes the gate; the
16
+ default executor then chooses the runtime itself, and the plugin still never writes `pwshPath`.
17
+
18
+ ## Verified environment
19
+
20
+ - Windows 10/11.
21
+ - Verified on DSH **0.2.1-rc.3** (see `dsh.verifiedAgainst` in `package.json`).
22
+ - The script path needs Microsoft Store; the manual path only needs the Store page.
23
+ - No administrator rights are required.
24
+
25
+ ## What it does not do
26
+
27
+ - It does not install anything at startup.
28
+ - It does not write the executor's `pwshPath`; it only writes a marked dsh-ops compatibility override when coexistence is on.
29
+ - It does not uninstall a Store-installed PowerShell 7.
30
+ - It does not auto-detect VPNs or preflight metered connections.
31
+
32
+ ## Coexistence with dsh-ops
33
+
34
+ On by default. The `dsh-ops-compat` override in `cordis.patch.yml` owns the enabled state: while
35
+ the dsh-win-pwsh7 bundle is installed and comes after dsh-ops, a DSH restart restores the native
36
+ `pwsh` tool and keeps `ops_bash`. Turning the switch off writes only a marked disable override;
37
+ removing the bundle removes the enable override, and cleanup best-effort removes the disable one.
38
+
39
+ The 0.2.1 bridge planned from the post-restriction visible set, which made it hide and restore the
40
+ same tools in a loop and starved the Host main thread. 0.2.2 plans from the pre-restriction
41
+ `knownNames` / `restrictableNames` and filters names `tools.restrict()` does not accept. The
42
+ multi-session validation passed, so the switch is default-on again.
43
+
44
+ ## Install this plugin
45
+
46
+ ```powershell
47
+ dsh plugin --profile desktop add github:XinSpark/dsh-win-pwsh7#v0.2.3
48
+ # or
49
+ dsh plugin --profile desktop add dsh-win-pwsh7
50
+ ```
51
+
52
+ Restart DSH after installing. To wire a path, paste the exact profile patch printed by
53
+ `pwsh7_status` into `~\.dsh\profiles\<profile>\cordis.patch.yml` and restart; the plugin never writes
54
+ the executor's `pwshPath` itself.
55
+
56
+ ## Two install paths
57
+
58
+ | Path | Behavior |
59
+ |---|---|
60
+ | **Script install** | Call `pwsh7_install`. The tool first requires the user to confirm no VPN is active (`vpnOffConfirmed: true`). It then opens the Microsoft Store page for PowerShell 7, shows a notice (do not close the Store window; cancel if a VPN is on), and invokes the localized Install / Get / 获取 / 安装 button with Windows UI Automation. |
61
+ | **Manual install** | Win+R → paste `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D` → Enter → click Install. A manual install is detected automatically; no pin is needed. |
62
+
63
+ If the Store page exposes no Install button within 90 seconds, the report asks the user to confirm
64
+ the VPN first: disconnect it, fully quit and restart Microsoft Store, then retry or use the manual
65
+ path.
66
+
67
+ ### Three-failure breaker
68
+
69
+ Within one Host process, after **3 consecutive failed script installs**:
70
+
71
+ - the next `pwsh7_install` starts no job;
72
+ - it first asks the user to check the network, especially the VPN;
73
+ - only after the user explicitly says it is fixed may the model call with
74
+ `userConfirmedFixed: true`; that resets the counter and allows one retry;
75
+ - three more failures open the breaker again;
76
+ - otherwise the report points at the manual install.
77
+
78
+ The count lives in the current process and resets when DSH restarts.
79
+
80
+ ## Configuration
81
+
82
+ | Key | Default | Meaning |
83
+ |---|---|---|
84
+ | `overridePath` | unset | Priority ① explicit `pwsh.exe`. It must spawn and report PowerShell 7+; a failed validation falls back to auto-detection. **It only changes probing and recommendations, not executor wiring.** |
85
+ | `dshOpsCompat` | `true` | Coexist with dsh-ops (on by default): writes and enables its Compatibility mode, hides duplicate official tools, and keeps `pwsh` + `ops_bash`. Requires a restart. |
86
+
87
+ `overridePath` is `volatile()`: the plugin-page configuration section saves it live; it can also be
88
+ set in the profile patch:
89
+
90
+ ```yaml
91
+ - id: win-pwsh7
92
+ config:
93
+ overridePath: 'D:\tools\pwsh\pwsh.exe'
94
+ ```
95
+
96
+ ## Recommended companion
97
+
98
+ When the script install completes, its job verdict contains an **instruction to the model**: tell the
99
+ user that dsh-win-pwsh7 works best paired with the **dsh-ops** plugin
100
+ ([https://github.com/T-Auto/dsh-ops](https://github.com/T-Auto/dsh-ops)); ask before installing it and
101
+ never install it without the user's consent. Command:
102
+
103
+ ```powershell
104
+ dsh plugin --profile <profile> add dsh-ops
105
+ ```
106
+
107
+ The recommendation appears only in the completed script-install result; `pwsh7_status` and manual
108
+ installs do not advertise it repeatedly.
109
+
110
+ ## Report example
111
+
112
+ `pwsh7_status` returns the text report below; the plugin page renders the same state as a card
113
+ rather than showing the whole report.
114
+
115
+ ```
116
+ Executor rows:
117
+ pwsh-sandbox : pwshPath = C:\Users\...\WindowsApps\Microsoft.PowerShell_8wekyb3d8bbwe\pwsh.exe (PowerShell 7.6.9, Store family alias)
118
+
119
+ The pwsh tool currently runs on PowerShell 7.6.9 via the executor (…).
120
+ The executor resolves to the version-stable Store family alias: … Nothing to configure.
121
+ ```
122
+
123
+ When nothing usable exists, the report ends with `Three ways forward:`: install / configure the
124
+ executor / disable the plugin.
125
+
126
+ ## Plugin page
127
+
128
+ The status section is a native card showing the state, runtime and executor path, with Re-detect
129
+ at the top right; the configuration section exposes `overridePath` and the default-on "Keep native
130
+ pwsh alongside dsh-ops" native switch.
131
+
132
+ ## Disable or uninstall
133
+
134
+ Disabling the plugin removes the `pwsh` gate and the plugin page; it does not uninstall PowerShell 7.
135
+ As long as `pwsh.exe` remains on the machine, the default executor still prefers it (normally the
136
+ Store build); Windows PowerShell 5.1 is used only when no `pwsh.exe` can be found. To truly return
137
+ to 5.1, uninstall the Store build or pin the executor to `powershell.exe` (not recommended).
138
+
139
+ ## Troubleshooting
140
+
141
+ | Symptom | What to do |
142
+ |---|---|
143
+ | The Store page opens without an Install button | Confirm the VPN is off, fully quit and restart Microsoft Store, then retry; or use the manual path. |
144
+ | `pwsh` still runs 5.1 | Run `pwsh7_status`, paste the reported `pwshPath` patch into the profile, and restart; or install the Store build. |
145
+ | The plugin page is missing | Confirm `dsh-win-pwsh7` is enabled in the current profile, then restart DSH. |
146
+
147
+ ## Verification
148
+
149
+ ```powershell
150
+ $env:DSH_APP_DIR = 'E:\FXS\APP\DSH NEXT\resources\app.asar'
151
+ node tools/check-host.mjs
152
+ node tools/check-client.mjs
153
+ node tools/check-native-contracts.mjs
154
+ ```
155
+
156
+ All three gates run offline against a packed installation (`resources/app.asar`); `DSH_APP_DIR`
157
+ accepts the install root, `resources/app`, or `resources/app.asar`.
158
+
159
+ ## Design constraints
160
+
161
+ - The reading comes from the executor's own `ctx.shell.pwshPath`, not from config rows;
162
+ `overridePath` only changes probing and recommendations.
163
+ - No VPN auto-detection and no metered-connection preflight: the user confirms the VPN before an
164
+ install, and a page-load failure asks again.
165
+ - No automatic retries; the three-failure breaker ends the loop.
166
+ - Run all three gates before publishing; end-to-end installation is release evidence, not part of
167
+ the automated gate.
168
+
169
+ ## License
170
+
171
+ [MIT](LICENSE)