@toddzheng024/dscode-desktop 0.7.33 → 0.7.34

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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  DSCODE adds a coding preset, browser workflows, cross-session collaboration,
4
4
  memory and task management to the official DeepSeek Harness Desktop application.
5
5
 
6
- **Version:** 0.7.33 · **Harness runtime:** 0.2.0-rc.2
6
+ **Version:** 0.7.34 · **Harness runtime:** 0.2.0-rc.2
7
7
 
8
8
  Preview release candidate; publication is a separate step.
9
9
 
@@ -47,7 +47,7 @@ public release; a candidate build alone does not publish a package.
47
47
  ## Install a published version
48
48
 
49
49
  After this version is available on npm, enter
50
- `@toddzheng024/dscode-desktop@0.7.33` in **Plugins → Add plugin**,
50
+ `@toddzheng024/dscode-desktop@0.7.34` in **Plugins → Add plugin**,
51
51
  review the installation, install, then choose **Enable now**. Using the exact
52
52
  version keeps the installation tied to these instructions and compatibility
53
53
  requirements. The `preview` tag tracks the current Desktop preview release.
package/docs/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@ Per-release notes live in [`docs/releases/`](releases/); the entries below summa
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.7.34] - 2026-10-08
12
+
13
+ - Fix Desktop activation without an exported `DSH_HOME`: workspace discovery, email, scheduling and automatic review now resolve the native Harness home, including the default `~/.dsh`, blank overrides and supported tilde paths.
14
+ - Keep browser state, memory, session communication/cards, delegation, review baselines, provider caches, diagnostics and usage records under that same resolved home. The terminal launcher continues to map its `DSCODE_HOME` into `DSH_HOME`; explicit memory and audit directories remain supported. Credentials and mail retain their existing account-store locations. This change does not move previously misplaced state automatically.
15
+ - Public Desktop installation qualification now runs with `DSH_HOME` absent and requires all four affected components to activate. The native Host and signed macOS lifecycle verifiers also support this default-home scenario.
16
+ - The demo guide separates DSCODE's workflow from the Hub's discovery contribution and adds a Desktop walkthrough draft with setup, reset and qualification boundaries. The existing terminal recording instructions remain available. Release-note links also work when read on GitHub Releases or as downloaded Markdown.
17
+ - Desktop Hub publication retries the npm propagation window where the exact version is visible but the Hub still reports `package_not_found` or an older index. Retries are bounded; policy/credential failures and integrity mismatches still stop publication.
18
+
11
19
  ## [0.7.33] - 2026-10-08
12
20
 
13
21
  - The formal Desktop plugin is `@toddzheng024/dscode-desktop`, with an icon, localized identity, exact Harness compatibility and package-specific instructions. Public release candidates target Harness 0.2.0-rc.2 on macOS Apple Silicon; standalone applications and an automatic Desktop updater remain outside this delivery.
@@ -52,8 +52,9 @@ running. A new start gets fresh page IDs; interrupted actions are never replayed
52
52
  | `/browser use extension` | Experimental: share selected existing Chrome tabs through the DSCODE extension, without enabling a browser-wide remote-debugging port. |
53
53
 
54
54
  Stop all session browsers before changing defaults. These settings persist in
55
- `browser/config.json` under `DSH_HOME` (or `DSCODE_HOME`, falling back to
56
- `~/.dscode`). They apply to future starts. The agent cannot change browser mode
55
+ `browser/config.json` under the native Harness home: a nonblank `DSH_HOME`,
56
+ otherwise `~/.dsh`. The terminal launcher maps `DSCODE_HOME` into `DSH_HOME`.
57
+ They apply to future starts. The agent cannot change browser mode
57
58
  through a tool. Dedicated profiles are separate from personal Chrome profiles;
58
59
  log in directly in the visible window when needed. The agent should keep that
59
60
  tab and hand control to you for login or 2FA, then inspect the same page again.
@@ -623,8 +624,8 @@ The source checkout can also package the DSCODE agent preset, custom-model
623
624
  and account settings, browser preview, durable session communication, cross-session
624
625
  memory and opt-in task scheduling together. Ordinary builds remain private;
625
626
  the separate [release process](hub-distribution.md#desktop-release-candidate-and-publication)
626
- qualifies a public candidate for 0.2.0-rc.2. Public npm distribution is still
627
- pending. The package does
627
+ qualifies a public candidate for 0.2.0-rc.2. The Desktop preview is published on npm as
628
+ `@toddzheng024/dscode-desktop@0.7.34`. The package does
628
629
  not yet include all DSCODE Host services. Its experimental
629
630
  [native Computer Use adapter](computer-use.md) has passed helper and lifecycle
630
631
  checks; window observation, screenshots and input await qualification with
@@ -632,7 +633,7 @@ macOS Accessibility permission.
632
633
  Choose this package or the browser-only bundle; enabling both duplicates their
633
634
  browser services.
634
635
 
635
- The current candidate's package name is `@toddzheng024/dscode-desktop`. It also
636
+ The Desktop plugin's package name is `@toddzheng024/dscode-desktop`. It also
636
637
  includes [Plugin Hub](plugin-hub.md) for community discovery and installation
637
638
  through the official manager. The earlier private
638
639
  `@toddzheng024/dscode-desktop-preset-probe` must be removed before installing
package/docs/demo.md CHANGED
@@ -1,15 +1,97 @@
1
- # The 90-second demo
1
+ # Demo guide
2
2
 
3
- Most coding agents work alone. This script shows the three things DSCODE does
4
- instead — sessions that can see each other, an independent reviewer for code and
5
- for approvals, and side questions that never disturb the main conversation — in
6
- an order a stranger can follow.
3
+ ## Desktop feature summary
4
+
5
+ The 0.7.34 Desktop preview installs into official Harness Desktop. Its qualified
6
+ target is macOS Apple Silicon with Harness runtime **0.2.0-rc.2**. Present the
7
+ DSCODE workflow and the Plugin Hub contribution separately:
8
+
9
+ | Area | What the user gains | Boundary |
10
+ | --- | --- | --- |
11
+ | DSCODE coding workflow | A DSCODE preset with custom model/account settings, session communication, memory, usage and delegation views | Terminal-only panels retain their terminal UI; model behavior depends on the configured provider. |
12
+ | DSCODE browser workflow | Browser tools, per-site access, a screenshot preview and point annotations in the conversation | Chrome is required. An image-capable model is needed to interpret screenshots; captures expire and must be refreshed after navigation. |
13
+ | DSCODE optional services | Email inbox integration and scheduled work | Account configuration and explicit opt-in are required. Desktop must stay open for scheduled delivery. |
14
+ | Our Plugin Hub integration | Search and categories across our community catalog, package details, compatibility and reported scan information, plus read-only discovery tools for the agent | Catalog coverage defines the search scope. Reported scans do not establish that a plugin is safe. |
15
+ | Hub installation handoff | Review an exact npm version, confirm its installation and open official management | Official Harness inspects and installs it. New plugins remain disabled; official controls own enabling, configuration and removal. |
16
+
17
+ The Hub story is **find → understand → review → install → manage**. Its value is
18
+ community discovery and the information needed to choose a plugin. Do not count
19
+ DSCODE's browser, memory or coding features as Hub features, or claim that Hub
20
+ replaces official lifecycle management. This demo makes no claim that every
21
+ official Desktop version lacks search.
22
+
23
+ ## Five-minute Desktop walkthrough draft
24
+
25
+ This draft is for existing DSH Desktop users. Choose the final emphasis and
26
+ recording length with the presenter; allow separate time for downloads and
27
+ account setup. The installation and UI paths have verification records, but
28
+ this complete timed walkthrough has not been rehearsed with a live model.
29
+
30
+ ### Prepare
31
+
32
+ 1. Use a disposable Desktop profile and a scratch workspace. Install
33
+ `@toddzheng024/dscode-desktop@0.7.34` through **Plugins → Add plugin**, then
34
+ choose **Enable now**. Select the **DSCODE** preset. Follow the
35
+ [installation guide](browser-use.md#install-the-experimental-combined-desktop-package)
36
+ if migrating from the old probe package.
37
+ 2. Configure a model in private, confirm one short response and install Chrome.
38
+ For the annotation segment, choose a model whose configuration accepts images.
39
+ Do not record API keys, account login or personal workspace contents.
40
+ 3. Open **Settings → Plugin Hub**, search for `dsh-theme-plugin`, and check the
41
+ current details and compatibility. The recorded installation check used
42
+ **0.3.3**; the live catalog may offer a newer exact version. Rehearse that
43
+ version before using it in a recording. An incompatible or unavailable entry
44
+ is a reason to show discovery only, without claiming installation succeeded.
45
+ 4. Keep the [0.7.34 release page](https://github.com/qiz029/dscode/releases/tag/v0.7.34)
46
+ ready for the installation and distribution closing shot. The public npm
47
+ package and GitHub archive carry the same qualified bytes.
48
+
49
+ ### Walkthrough
50
+
51
+ | Approximate time | Action | Point to explain |
52
+ | --- | --- | --- |
53
+ | 0:00–0:30 | Show the enabled DSCODE plugin and a DSCODE session in official Desktop. | Existing DSH Desktop users can add DSCODE through a plugin. |
54
+ | 0:30–1:30 | Open Plugin Hub, search, choose a category and open details. | Our Hub brings community discovery, descriptions and compatibility information into the application. |
55
+ | 1:30–2:30 | Choose **Review installation**, show the exact package/version, then **Confirm installation**. Wait for its actual outcome. | The user reviews the package; official Harness performs installation. Download time is additional. |
56
+ | 2:30–3:00 | Choose **Manage installed plugin**, show its disabled state and official controls. | Enabling, configuration and removal stay in official management. Enabling the example theme is optional. |
57
+ | 3:00–4:30 | In the DSCODE session, ask: “Use the browser to open https://example.com and inspect the page.” Grant that origin if requested. In **Browser preview**, refresh the image, select a point, type “Summarize the text at this point” and choose **Send annotation**. | This is DSCODE's browser workflow. The screenshot, page reference and selected point enter the conversation; show the real response only after it arrives. |
58
+ | 4:30–5:00 | Show the exact npm install specification and GitHub release assets/notes. | Users can install the released plugin today; releases include compatibility, changes and verification limits. |
59
+
60
+ For a Hub-only cut, keep the first four rows and the release closing shot. No
61
+ model account is needed to browse and install through the Hub UI. For a longer
62
+ DSCODE cut, add a scratch-project coding task and demonstrate the relevant
63
+ usage/delegation view after rehearsing it with the selected model.
64
+
65
+ ### Rehearsal and reset
66
+
67
+ - Confirm the selected package is absent before the install shot. Remove a
68
+ previous demo installation through official **Plugins** in the disposable
69
+ profile, then reopen Hub. Do not remove plugins from a personal profile to
70
+ prepare the demonstration.
71
+ - Show success, cancellation, refusal or restart-required exactly as reported.
72
+ After a Host restart, inspect official **Plugins** before attempting another
73
+ installation; Hub operation history does not survive the restart.
74
+ - If the Hub/network is unavailable, use a clearly identified earlier recording
75
+ or continue with the DSCODE segment. Do not label cached footage as a live
76
+ successful install.
77
+ - Refresh the browser screenshot and select a new point if the capture expires
78
+ or navigation occurs. Text-only delivery must be described as text-only.
79
+ - Keep standalone DMG delivery, coordinated update/changelog UI, Profile
80
+ rollback and native Computer Use permission qualification out of this demo's
81
+ claims. See [Plugin Hub](plugin-hub.md), [Browser use](browser-use.md) and the
82
+ [release notes](releases/0.7.34.md) for the shipped behavior and limits.
83
+
84
+ ## The 90-second terminal demo
85
+
86
+ The terminal script shows sessions that can see each other, an independent
87
+ reviewer for code and approvals, and side questions that leave the main
88
+ conversation's context unchanged.
7
89
 
8
90
  The recording lands in `assets/demo.gif`; put that image directly under the
9
- README tagline. Every command below is real, so the demo is reproducible: the
10
- only unpredictable part is the model's own wording.
91
+ README tagline. Rehearse the commands below with the selected model before
92
+ recording: response quality and timing are not established by this script.
11
93
 
12
- ## Record it
94
+ ### Record it
13
95
 
14
96
  ```sh
15
97
  brew install asciinema agg
@@ -39,12 +121,12 @@ Before you hit record:
39
121
  or full home paths). Any interface language works; the captions below are
40
122
  English.
41
123
 
42
- ## Shot list
124
+ ### Shot list
43
125
 
44
126
  | Time | Screen | Do this | Say this (captions) |
45
127
  |---|---|---|---|
46
128
  | 0:00–0:06 | Shell in the scratch repo | `dscode` | "A coding agent in your terminal — macOS, local, one pinned harness." |
47
- | 0:06–0:22 | TUI, main session | Type `Add a bounds check to parse() in parser.ts and run the tests.` | "It reads the file, edits it, runs the tests." |
129
+ | 0:06–0:22 | TUI, main session | Type `Trim whitespace and ignore empty fields in parse() in parser.ts. Add and run tests.` | "It reads the file, edits it, runs the tests." |
48
130
  | 0:22–0:36 | Same terminal | `/btw why would the first turn have a cold cache?` — leave the main turn running | "A side question runs in its own read-only child session. The answer never enters the main conversation." |
49
131
  | 0:36–0:56 | Second terminal | `dscode sessions` then `dscode send <the other session id> --steer "review the change in parser.ts and reply with what you would change"` | "Two sessions on one machine can find each other, hand work over and answer." |
50
132
  | 0:56–1:06 | Back in the first terminal | Show the reply arriving (`/mailbox`, or the agent's `read_session`/`send_session` tools if you asked it to hand the review over itself) | "The answer comes back as a message, not as a new task." |
@@ -60,7 +142,7 @@ Two beats worth keeping if you have the seconds:
60
142
  - `/agents` at the end: the `/btw` child session is a real, read-only session,
61
143
  not a hidden prompt.
62
144
 
63
- ## Reproduce it without recording
145
+ ### Reproduce it without recording
64
146
 
65
147
  ```sh
66
148
  npm i -g @toddzheng024/dscode
@@ -33,6 +33,23 @@ The default state directory is `~/.local/share/dscode-hub`, overridable with `DS
33
33
 
34
34
  A user who already has dsh-hub installed can apply directly, but must use pnpm 10.15.1, set a dedicated `DSH_HOME` and `DSH_AGENTS_HOME`, and set `npm_config_ignore_scripts=true` on the install command. The launcher is recommended because it unifies those conditions.
35
35
 
36
+ ### Desktop users
37
+
38
+ Install the official Harness Desktop application with embedded runtime
39
+ 0.2.0-rc.2. In **Plugins → Add plugin**, enter
40
+ `@toddzheng024/dscode-desktop@0.7.34`, review and install, then choose
41
+ **Enable now**. Select the DSCODE preset and configure your model account.
42
+ The Desktop preview is qualified on macOS Apple Silicon. See
43
+ [Desktop installation](browser-use.md#install-the-experimental-combined-desktop-package)
44
+ for archive checksums, migration and removal, and [Plugin Hub](plugin-hub.md)
45
+ for discovering community plugins after installation.
46
+
47
+ Version 0.7.34 fixes the 0.7.33 activation error “4 entries did not activate”
48
+ when Desktop starts without `DSH_HOME`. Update to the exact 0.7.34 package and
49
+ enable it again; exporting a shell variable is unnecessary. Default Desktop
50
+ state uses `~/.dsh`, as resolved by Harness. Publication now verifies installation
51
+ and the affected components with that default environment.
52
+
36
53
  ## Release checklist
37
54
 
38
55
  Cutting a release from this repository, in order. The rule that matters most: **the git tag must equal `v<package.json version>`, and that version must not already be on npm from a different build.**
@@ -142,7 +159,12 @@ npm run publish:desktop -- verify
142
159
  Each phase checks the archive and receipt again. The npm phase publishes with
143
160
  `--tag preview`; an existing version is reusable only with identical integrity.
144
161
  The Hub phase syncs the package and requires the exact version, integrity and
145
- Desktop compatibility to resolve. The final phase downloads public bytes,
162
+ Desktop compatibility to resolve. A newly published package can appear on
163
+ the npm version endpoint before the Hub can fetch its package index. In this
164
+ post-publication phase, `package_not_found` and an index missing the exact
165
+ version retry up to 40 times, 15 seconds apart. Other rejections and credential
166
+ errors stop immediately; the final integrity and compatibility checks still
167
+ apply. The final phase downloads public bytes,
146
168
  checks their SHA-256, and installs the exact npm version into a new native
147
169
  Harness profile before booting it and checking the preset and Hub RPC. The
148
170
  result is saved as `artifacts/local/desktop-public-install.json`. No model
package/docs/memory.md CHANGED
@@ -59,7 +59,7 @@ renderer interaction with the slash commands.
59
59
 
60
60
  A SQLite lease coordinates multiple processes and the background holds a heartbeat; a source session under recovery, an expired lease and a clear operation all stop an old result from committing. The consolidation model has no shell, MCP, file-write or delegation tools, and file paths are determined by the plugin.
61
61
 
62
- Directory resolution order: plugin `root` → `DSCODE_MEMORY_HOME` → `$DSCODE_HOME/memories` → `$DSH_HOME/memories` → `~/.local/share/dscode-hub/memories`. A source launch normally uses the project's `.runtime/memories`, and an npm launch uses the launcher's state directory. To share memory between installations, set the same `DSCODE_MEMORY_HOME` explicitly.
62
+ Directory resolution order: plugin `root` → `DSCODE_MEMORY_HOME` → the native Harness home plus `memories`. Harness resolves a nonblank `DSH_HOME` (including supported tilde paths), otherwise `~/.dsh`. A source launch normally sets the project's `.runtime` as that home; the npm launcher maps its `DSCODE_HOME` into `DSH_HOME`. To share memory between installations, set the same `DSCODE_MEMORY_HOME` explicitly. Version 0.7.34 does not automatically move memory left in an earlier fallback directory.
63
63
 
64
64
  ```text
65
65
  memories/
@@ -1,6 +1,6 @@
1
1
  # Plugin Hub in Harness Desktop
2
2
 
3
- The DSCODE Desktop candidate includes **Settings → Plugin Hub**, backed by
3
+ The DSCODE Desktop plugin includes **Settings → Plugin Hub**, backed by
4
4
  [DSH Plugin Hub](https://dshpluginhub.ai). It uses the same public catalog as
5
5
  the Hub website and CLI. No Hub login is required to browse.
6
6
 
@@ -59,10 +59,10 @@ retrying; the Hub's operation history is not persisted.
59
59
  - Search queries and package lookups go to `api.dshpluginhub.ai`. Provider keys,
60
60
  conversation text, workspace files and local credentials are not sent by this
61
61
  catalog client. The agent may include task terms in a query you ask it to make.
62
- - This guide describes the source candidate. Public Desktop npm distribution is still
63
- being qualified. Official Add plugin installation, Hub installation with
62
+ - The Desktop plugin is published as `@toddzheng024/dscode-desktop@0.7.34`
63
+ on npm, with the `preview` tag. Official Add plugin installation, Hub installation with
64
64
  handoff to official management, and migration from the legacy probe name
65
65
  have passed on macOS Desktop 0.2.0-rc.2.
66
66
 
67
- See [Desktop candidate installation](browser-use.md#install-the-experimental-combined-desktop-package)
67
+ See [Desktop installation](browser-use.md#install-the-experimental-combined-desktop-package)
68
68
  and [distribution](hub-distribution.md) for package setup and release status.
@@ -1,12 +1,12 @@
1
1
  # DSCODE 0.7.33
2
2
 
3
- This release candidate packages DSCODE for official Harness Desktop, adds community plugin discovery through our Plugin Hub, and establishes a separate Desktop publication path alongside the terminal release.
3
+ This release packages DSCODE for official Harness Desktop, adds community plugin discovery through our Plugin Hub, and establishes a separate Desktop publication path alongside the terminal release.
4
4
 
5
5
  - **Formal Desktop plugin:** `@toddzheng024/dscode-desktop` has an icon, English/Chinese display metadata, package-specific instructions and an exact Harness peer version. It adds coding, browser preview, collaboration, memory, email and opt-in scheduling to the official application. The qualified target is macOS Apple Silicon with Harness runtime `0.2.0-rc.2`. Other runtime candidates remain private. A standalone application, DMG and automatic Desktop updates are deferred.
6
6
  - **Plugin Hub:** native Settings and read-only conversation tools share the Hub API and schema. Search, categories, numbered pagination and details expose compatibility and reported security information. Installation requires an exact npm version, official inspection and explicit confirmation. New plugins remain disabled, and the UI opens official management for enabling, configuration and removal. Git-only entries remain visible without installation; Hub Profile application and rollback are outside this integration. A reported scan result is not a guarantee about plugin behavior.
7
7
  - **Installation and migration:** official Add plugin accepts the archive, followed by Enable now. Users of the earlier private probe remove its old package name before installing the formal package, because both register the same components. The migration check verifies preserved sessions, credentials, configuration and user patches; keep a backup for your own state. Ordinary same-name replacement, incompatible-update refusal, removal and reinstallation have separate lifecycle checks.
8
8
  - **Reproducible distribution steps:** an independent Desktop runtime builds a public candidate, then the exact archive is unpacked and exercised through native Host startup/restart and browser probes. Publication verifies its checksums, version, compatibility and qualification receipt, publishes with the npm `preview` tag, synchronizes the Hub and checks the served public bytes. GitHub assets include the archive, checksum, installation README and release notes. Public availability is established by successful publication and registry verification, not by a local build.
9
- - **Browser and model workflows:** this version includes the browser and custom-provider work accumulated since 0.7.32. Browser access uses site and Developer grants, document-bound WebMCP discovery, explicit handoff and retained tabs. Desktop previews keep annotation drafts across navigation and permission changes while rejecting expired or replaced documents. Custom services support Chat Completions, Responses and Anthropic Messages, optional images, manual model limits and cancellable discovery/tests. The detailed fixes and boundaries are recorded in the [changelog](../CHANGELOG.md).
9
+ - **Browser and model workflows:** this version includes the browser and custom-provider work accumulated since 0.7.32. Browser access uses site and Developer grants, document-bound WebMCP discovery, explicit handoff and retained tabs. Desktop previews keep annotation drafts across navigation and permission changes while rejecting expired or replaced documents. Custom services support Chat Completions, Responses and Anthropic Messages, optional images, manual model limits and cancellable discovery/tests. The detailed fixes and boundaries are recorded in the [changelog](https://github.com/qiz029/dscode/blob/main/docs/CHANGELOG.md).
10
10
 
11
11
  Verification: `npm run check` passed on the release implementation (1,067
12
12
  unit/component cases, 79.99% full-source line coverage, both TypeScript checks,
@@ -23,8 +23,15 @@ The official macOS application separately installed and enabled the private
23
23
  formal archive, and its Hub installed `dsh-theme-plugin@0.3.3` disabled and
24
24
  opened official management. A native lifecycle check migrated the old probe
25
25
  name and preserved state. Those rendered GUI/migration observations used the
26
- private 0.7.32 candidate. Public npm publication and the post-publication
27
- installation check remain pending; the retained local receipts do not prove
28
- that users can already download this version from the registry.
26
+ private 0.7.32 candidate. The launcher, terminal bundle and Desktop plugin were subsequently published
27
+ to npm at 0.7.33, and the Hub lists both the terminal profile and Desktop plugin.
28
+ The final release run passed 1,068 unit/component cases at 79.98% coverage and
29
+ all other maintained gates. Public Desktop installation, the installed Host
30
+ and Hub RPC passed in CI. A separate fresh public terminal installation passed
31
+ startup configuration, terminal imports and Hub doctor. The GitHub Desktop
32
+ archive and checksum match the qualified npm bytes. The first two publication
33
+ attempts encountered npm propagation delay; rerunning the same verified
34
+ artifacts completed publication without changing versions or bytes. Detailed
35
+ receipts and boundaries are recorded in [Verification](https://github.com/qiz029/dscode/blob/main/docs/verification.md).
29
36
 
30
37
  Unverified paths: real-model task quality, native Computer Use screenshot/input with macOS Accessibility permission, other OS/architecture/runtime combinations, and third-party plugin behavior after enabling. General remote HTTP MCP retains separate runtime compatibility/security work. Scripted model fixtures and browser probes do not establish live model quality. Demo preparation is deferred until distribution is ready.
@@ -0,0 +1,24 @@
1
+ # DSCODE 0.7.34
2
+
3
+ This release fixes Desktop plugin activation during a normal application launch without an exported `DSH_HOME`.
4
+
5
+ - **Default Desktop home:** workspace discovery, email, scheduling and automatic permission review previously refused to activate unless `DSH_HOME` was explicitly set. They now use the same native home resolver as Harness: a nonblank override, including supported tilde paths, or `~/.dsh`. Users seeing “4 entries did not activate” in 0.7.33 can install `@toddzheng024/dscode-desktop@0.7.34` and enable it again. No shell environment setup is required.
6
+ - **Consistent local state:** browser permissions/configuration, memory, session cards and messaging, delegation, review baselines, provider caches, diagnostics and usage resolve that same home. Explicit memory/audit directories remain supported, and the terminal launcher still translates `DSCODE_HOME` into its native Harness home. Shared account credentials and mail keep their existing locations. Old fallback data is not migrated automatically.
7
+ - **Installation qualification:** public installation now deliberately omits `DSH_HOME` and checks workspace, email, scheduling and automatic-review activation. A package cannot pass the publication gate using only an explicitly configured home. Native Host and signed macOS lifecycle verifiers also accept a default-home scenario. The earlier 0.7.33 checks set `DSH_HOME`, which concealed this activation failure.
8
+ - **Distribution maintenance:** this release includes bounded retries for npm-to-Hub propagation and the Desktop/Hub demo guide added after 0.7.33. Versioned release notes, checksums, compatibility and installation instructions continue to ship with the package.
9
+
10
+ Verification: the default-home native Host reproduced the original activation
11
+ failure before the fix and passed startup/restart after it. The signed macOS
12
+ application passed installation, upgrade, incompatible-update refusal, removal
13
+ and reinstallation with `DSH_HOME` absent, including state preservation and
14
+ browser checks. `npm run check` passed 1,073 unit/component cases at 80.17%
15
+ full-source line coverage, both TypeScript checks, native integration, package
16
+ checks and 63 eval cases. Eight focused home-resolution/release-gate tests also
17
+ passed. The exact Desktop candidate passed native Host startup/restart and
18
+ browser qualification. Installation receipts and public release status are
19
+ recorded in the [verification record](https://github.com/qiz029/dscode/blob/main/docs/verification.md).
20
+
21
+ The Desktop target remains macOS Apple Silicon with Harness runtime 0.2.0-rc.2.
22
+ Live-model task quality, native Computer Use Accessibility qualification and
23
+ other platform/runtime combinations are not established by these checks. This
24
+ release does not provide a standalone DMG or migrate old fallback data.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toddzheng024/dscode-desktop",
3
- "version": "0.7.33",
3
+ "version": "0.7.34",
4
4
  "private": false,
5
5
  "description": "DSCODE for Harness Desktop: coding collaboration, session memory, browser workflows and task management.",
6
6
  "license": "MIT",
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import z from '@deepseek-ai/schemastery';
2
3
  import { BlockAssembler, createSystemMessage, createUserMessage } from '@deepseek-ai/dsh-llm';
3
4
  import { REVIEW_POLICY, escalationDiagnosticGrant, needsMcpApproval, redact, fingerprint, contextFor, parseDecision } from './policy.mjs';
@@ -22,8 +23,7 @@ export const Config = z.object({
22
23
 
23
24
  export function apply(ctx, config) {
24
25
  if (Boolean(config.provider) !== Boolean(config.model)) throw new Error('Auto review requires both provider and model, or neither');
25
- if (!config.auditDirectory && !process.env.DSH_HOME) throw new Error('Auto review requires DSH_HOME or auditDirectory');
26
- const audit = auditStore(config.auditDirectory ?? join(process.env.DSH_HOME, 'auto-review'));
26
+ const audit = auditStore(config.auditDirectory ?? join(resolveDshHome(), 'auto-review'));
27
27
  const calls = new WeakMap();
28
28
  const budgets = new WeakMap();
29
29
  const mode = agent => ctx.permissionPresets.current(agent.session);
@@ -1,9 +1,9 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { createHash, randomUUID } from 'node:crypto';
2
3
  import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
3
- import { homedir } from 'node:os';
4
4
  import path, { join } from 'node:path';
5
5
 
6
- export const browserHome = () => join(process.env.DSH_HOME ?? process.env.DSCODE_HOME ?? join(homedir(), '.dscode'), 'browser');
6
+ export const browserHome = () => join(resolveDshHome(), 'browser');
7
7
  export const DEFAULT_CONFIG = Object.freeze({ mode: 'persistent', headless: false });
8
8
 
9
9
  export function validateConfig(input, paths = path) {
@@ -1,7 +1,7 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { execFile } from 'node:child_process';
2
3
  import { createHash, randomUUID } from 'node:crypto';
3
4
  import { lstat, mkdir, readdir, realpath, rm, writeFile } from 'node:fs/promises';
4
- import { homedir } from 'node:os';
5
5
  import { isAbsolute, join, relative, sep } from 'node:path';
6
6
  import { promisify } from 'node:util';
7
7
  import { reviewSpec } from './git.mjs';
@@ -17,7 +17,7 @@ const SENSITIVE_PATTERNS = ['.env', '.env.*', '.npmrc', '.pypirc', 'id_rsa', 'id
17
17
  const IDENTITY = { GIT_AUTHOR_NAME: 'dscode', GIT_AUTHOR_EMAIL: 'review@dscode.invalid', GIT_COMMITTER_NAME: 'dscode', GIT_COMMITTER_EMAIL: 'review@dscode.invalid' };
18
18
  export const snapshotLimits = { files: 20000, bytes: 256 * 1024 * 1024, fileBytes: 4 * 1024 * 1024 };
19
19
 
20
- const defaultRoot = () => join(process.env.DSH_HOME ?? process.env.DSCODE_HOME ?? join(homedir(), '.local/share/dscode-hub'), 'review-baselines');
20
+ const defaultRoot = () => join(resolveDshHome(), 'review-baselines');
21
21
  const anchored = path => `/${path.replace(/[\\*?[\]!#]/g, '\\$&').replace(/ $/, '\\ ')}`;
22
22
  const refPart = value => String(value).replace(/[^A-Za-z0-9_-]/g, '_');
23
23
  const refsFor = task => {
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { cacheReadRatio } from '../session-metrics/pricing.mjs';
2
3
  import { ensureOpenRouterModels } from '../openrouter/models.mjs';
3
4
 
@@ -70,7 +71,7 @@ export function fitsInWindow(totalTokens, modelInfo) {
70
71
 
71
72
  /** The route's threshold ratio, waiting for the OpenRouter listing when it has not loaded yet. */
72
73
  export async function pricedThresholdRatio(provider, model, now = Date.now()) {
73
- if (provider === 'openrouter') await ensureOpenRouterModels({ home: process.env.DSH_HOME, now });
74
+ if (provider === 'openrouter') await ensureOpenRouterModels({ home: resolveDshHome(), now });
74
75
  return thresholdForCacheRatio(cacheReadRatio(provider, model, now));
75
76
  }
76
77
 
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { homedir } from 'node:os';
3
4
  import { join, resolve } from 'node:path';
@@ -13,8 +14,7 @@ export const inject = ['agents', 'agentPresets', 'skills', 'sessionProjections',
13
14
 
14
15
  /** Session-owned discovery; Desktop's process cwd is not a workspace identity. */
15
16
  export function apply(ctx) {
16
- const stateDir = process.env.DSH_HOME;
17
- if (!stateDir) throw Error('Desktop workspace discovery requires DSH_HOME');
17
+ const stateDir = resolveDshHome();
18
18
  mkdirSync(join(stateDir, 'config'), { recursive: true });
19
19
  const installationHooks = join(stateDir, 'config/hooks.local.json');
20
20
  if (!existsSync(installationHooks)) writeFileSync(installationHooks, '{"hooks":{}}\n', { mode: 0o600, flag: 'wx' });
@@ -1,5 +1,5 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { existsSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { AsyncLocalStorage } from 'node:async_hooks';
@@ -108,7 +108,7 @@ export function apply(ctx, config = {}) {
108
108
  return { ...assembled, tools: assembled.tools.filter(tool => !hidden.has(tool.name)), sections: assembled.sections.filter(section => !hidden.has(section.name.replace(/^tool:/, ''))) };
109
109
  });
110
110
  const runningChildren = id => ctx.agents.list().filter(a => a.session.header.origin === 'subagent' && a.session.header.parentSession === id && a.status === 'running').length;
111
- const home = process.env.DSH_HOME ?? process.env.DSCODE_HOME ?? join(homedir(), '.local/share/dscode-hub');
111
+ const home = resolveDshHome();
112
112
  const board = new DelegateBoard({ root: join(home, 'delegate-boards'), childName: CHILD_NAME });
113
113
  const boardView = sessionId => board.view(sessionId, childId => ctx.agents.get(childId)?.status);
114
114
  ctx.effect(() => setBoardSource(sessionId => {
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { clientRequestSchema } from '@deepseek-ai/dsh-client-connection';
2
3
  import { DesktopEmail } from './desktop-service.mjs';
3
4
  import { createEmailContacts } from './contacts.mjs';
@@ -6,8 +7,7 @@ import { registerEmailTools } from '../email-tools/index.mjs';
6
7
 
7
8
  export const inject = ['agents', 'agentPresets', 'sessions'];
8
9
  export async function apply(ctx) {
9
- if (!process.env.DSH_HOME) throw Error('Desktop email requires DSH_HOME.');
10
- const service = new DesktopEmail({ home: process.env.DSH_HOME });
10
+ const service = new DesktopEmail({ home: resolveDshHome() });
11
11
  const contacts = createEmailContacts({ directory: service.inbox.directory });
12
12
  const sender = createEmailSender({ directory: service.inbox.directory,
13
13
  configurationHint: 'Open Email inbox → Mailbox connection and connect Gmail IMAP with an application password before sending. Gmail OAuth supports receiving only.' });
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
3
  import { join } from "node:path";
3
4
 
@@ -136,7 +137,7 @@ export function setGrokSubscription(next, at = Date.now()) {
136
137
  * instead of falling back to a bare tier name. One file read per refresh window keeps the
137
138
  * render path off the disk.
138
139
  */
139
- export function grokSubscriptionNow(home = process.env.DSH_HOME) {
140
+ export function grokSubscriptionNow(home = resolveDshHome()) {
140
141
  if (state !== undefined) return state;
141
142
  if (home === undefined) return undefined;
142
143
  if (disk !== undefined && disk.home === home && Date.now() - disk.at < GROK_SUBSCRIPTION_TTL_MS) return disk.value;
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import z from "@deepseek-ai/schemastery";
2
3
  import { RetryPolicySchema, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
3
4
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
@@ -72,7 +73,7 @@ export function apply(ctx, config = {}) {
72
73
  }
73
74
  };
74
75
  options();
75
- const home = process.env.DSH_HOME;
76
+ const home = resolveDshHome();
76
77
  /** The credential the route calls with, or undefined: the catalog must load without a login. */
77
78
  const resolveTokenSafe = async () => {
78
79
  const ref = options().apiKeyEnv;
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { grokAuthState, minutesLeft } from "./auth.mjs";
2
3
  import { grokSubscriptionNow, readGrokSubscription } from "./billing.mjs";
3
4
 
@@ -10,7 +11,7 @@ import { grokSubscriptionNow, readGrokSubscription } from "./billing.mjs";
10
11
  * @returns `{ status: { kind, expiresIn? }, subscription? }`; `kind` is the local login state
11
12
  * (`ready`, `expired`, `missing`, `malformed`) and `expiresIn` is minutes on a ready login.
12
13
  */
13
- export function grokStatusSnapshot({ home = process.env.DSH_HOME, now = Date.now() } = {}) {
14
+ export function grokStatusSnapshot({ home = resolveDshHome(), now = Date.now() } = {}) {
14
15
  const auth = grokAuthState({ now });
15
16
  const stored = grokSubscriptionNow() ?? (home === undefined ? undefined : readGrokSubscription(home));
16
17
  return {
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import z from '@deepseek-ai/schemastery';
2
3
  import { credentialRef } from '@deepseek-ai/dsh-credentials';
3
4
  import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment';
@@ -60,7 +61,7 @@ export function apply(ctx, config = {}) {
60
61
  const apiKey = await resolveKey();
61
62
  if (!apiKey || signal?.aborted) return undefined;
62
63
  const started = Date.now();
63
- const home = process.env.DSH_HOME, id = randomUUID();
64
+ const home = resolveDshHome(), id = randomUUID();
64
65
  const recipients = sessionId && home ? metricRecipients(ctx.get('agents'), sessionId) : [];
65
66
  const save = entry => {
66
67
  for (const recipient of recipients) {
@@ -1,4 +1,4 @@
1
- import { homedir } from 'node:os';
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
2
2
  import { resolve, join } from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { BlockAssembler, createUserMessage } from '@deepseek-ai/dsh-llm';
@@ -37,7 +37,7 @@ export function applyDesktop(ctx, options = {}) {
37
37
 
38
38
  function install(ctx, options, desktop = false) {
39
39
  const config = resolveConfig(options);
40
- const root = resolve(config.root ?? process.env.DSCODE_MEMORY_HOME ?? join(process.env.DSCODE_HOME ?? process.env.DSH_HOME ?? join(homedir(), '.local/share/dscode-hub'), 'memories'));
40
+ const root = resolve(config.root ?? process.env.DSCODE_MEMORY_HOME ?? join(resolveDshHome(), 'memories'));
41
41
  const store = new MemoryStore(root);
42
42
  const controller = new AbortController(), owner = randomUUID(), live = new Set(), started = new Set();
43
43
  let running, lastRoute, lastResult, lastSession;
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import z from '@deepseek-ai/schemastery';
2
3
  import { LlmError, RetryPolicySchema, resolveImageAttachmentAccess, resolveRetryPolicy } from '@deepseek-ai/dsh-llm';
3
4
  import { credentialRef } from '@deepseek-ai/dsh-credentials';
@@ -65,7 +66,7 @@ export function apply(ctx, config = {}) {
65
66
  }
66
67
  };
67
68
  options();
68
- const home = process.env.DSH_HOME;
69
+ const home = resolveDshHome();
69
70
  // The login lives in the shared credential store beside the provider keys.
70
71
  const grants = {
71
72
  read: async () => (await ctx.get('credentials')?.resolve(GRANT_REF))?.value || undefined,
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
3
  import { join } from 'node:path';
3
4
 
@@ -81,7 +82,7 @@ export async function currentGoUsage({ home, resolveAuth, fetch, now = Date.now(
81
82
  }
82
83
 
83
84
  /** The snapshot the status line renders: this process's own, else the file another process wrote. */
84
- export function goUsageNow(home = process.env.DSH_HOME, now = Date.now()) {
85
+ export function goUsageNow(home = resolveDshHome(), now = Date.now()) {
85
86
  if (state !== undefined) return state;
86
87
  if (home === undefined) return undefined;
87
88
  if (disk !== undefined && disk.home === home && now - disk.at < GO_USAGE_TTL_MS) return disk.value;
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import z from '@deepseek-ai/schemastery';
2
3
  import { LlmError, RetryPolicySchema, assertUsableApiKey, resolveImageAttachmentAccess, resolveRetryPolicy } from '@deepseek-ai/dsh-llm';
3
4
  import { credentialRef } from '@deepseek-ai/dsh-credentials';
@@ -73,7 +74,7 @@ export function apply(ctx, config = {}) {
73
74
  if (credentials !== undefined) return (await credentials.resolve(ref))?.value || undefined;
74
75
  return launchEnvironmentOf(ctx).get(ref)?.value || undefined;
75
76
  };
76
- const home = process.env.DSH_HOME;
77
+ const home = resolveDshHome();
77
78
  const ensureModels = () => ensureOpenRouterModels({ home });
78
79
  const adapter = new OpenRouterAdapter({
79
80
  displayName,
@@ -1,5 +1,4 @@
1
- import { homedir } from 'node:os';
2
- import { join } from 'node:path';
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
3
2
  import { communicationPanel, createCommunicationFeed, foldCommunication } from './tasks.mjs';
4
3
  import { SessionBridge } from './server.mjs';
5
4
  import { CommunicationService } from './communication.mjs';
@@ -8,7 +7,7 @@ import { discover, request } from './client.mjs';
8
7
  export const name = 'dscode-session-bridge';
9
8
  export const inject = ['agents', 'sessions', 'sessionTitle', 'sessionCards', 'commands', 'systemPrompt', 'tools'];
10
9
  export async function apply(ctx) {
11
- const home = process.env.DSH_HOME ?? process.env.DSCODE_HOME ?? join(homedir(), '.local/share/dscode-hub');
10
+ const home = resolveDshHome();
12
11
  const homeOption = `--home '${home.replaceAll("'", "'\\''")}'`;
13
12
  const bridge = new SessionBridge(ctx, home);
14
13
  await bridge.start();
@@ -1,5 +1,5 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { join } from 'node:path';
2
- import { homedir } from 'node:os';
3
3
  import { BlockAssembler, createUserMessage } from '@deepseek-ai/dsh-llm';
4
4
  import { SessionCards } from './manager.mjs';
5
5
  import { TOPIC_PROMPT } from './content.mjs';
@@ -8,7 +8,7 @@ import { effortFor } from '../providers/effort.mjs';
8
8
  export const name = 'dscode-session-cards';
9
9
  export const inject = ['sessions', 'llm'];
10
10
  export function apply(ctx, config = {}) {
11
- const home = process.env.DSH_HOME ?? process.env.DSCODE_HOME ?? join(homedir(), '.local/share/dscode-hub');
11
+ const home = resolveDshHome();
12
12
  const cards = new SessionCards({ root: join(home, 'session-cards'), config, generate: async (input, route, signal, sessionId) => {
13
13
  const assembler = new BlockAssembler(); let finished = false, usage;
14
14
  // Cards need little reasoning whatever the session runs at: low, or the nearest level the model offers.
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { randomUUID } from 'node:crypto';
2
3
  import { appendMetric } from './store.mjs';
3
4
  import { estimateCost, priceVersionFor } from './pricing.mjs';
@@ -30,7 +31,7 @@ export function apply(ctx) {
30
31
  snapshots.set(session, { seq: session.seq, value });
31
32
  return { ...value, currentTps: smoothedRate.get(session), requestActive };
32
33
  }));
33
- const home = process.env.DSH_HOME;
34
+ const home = resolveDshHome();
34
35
  // Remaining balance is best-effort decoration: resolve the key lazily (never
35
36
  // inject the credentials service, so a missing one cannot fail startup), keep
36
37
  // the request on a five-minute cache, and never let it reach the render path.
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { readMetrics } from './store.mjs';
2
3
  import { t } from '../i18n/messages.mjs';
3
4
  import { estimateCost, peakEmoji } from './pricing.mjs';
@@ -67,7 +68,7 @@ export function summarize(rows, events = [], corrupt = false) {
67
68
  * @returns `{ turn, cost, calls, unknown }` per completed or running turn.
68
69
  */
69
70
  export function turnCostsFor(id, events = []) {
70
- const entries = id && process.env.DSH_HOME ? readMetrics(process.env.DSH_HOME, id).rows : [];
71
+ const entries = id ? readMetrics(resolveDshHome(), id).rows : [];
71
72
  return attributeCostByTurn(entries, events).turns;
72
73
  }
73
74
  /**
@@ -229,7 +230,7 @@ export function formatFooter(metrics, context, columns = 80, rates, locale = 'en
229
230
  */
230
231
  export function sessionSpend(id) {
231
232
  try {
232
- const ledger = id && process.env.DSH_HOME ? readMetrics(process.env.DSH_HOME, id) : { rows: [], corrupt: false };
233
+ const ledger = id ? readMetrics(resolveDshHome(), id) : { rows: [], corrupt: false };
233
234
  const data = id ? source?.(id) : undefined;
234
235
  const summary = summarize(ledger.rows, data?.events ?? [], ledger.corrupt);
235
236
  return { cost: summary.cost, unknown: summary.unknown, pending: summary.pending };
@@ -245,7 +246,7 @@ export function footerFor(id, stats, columns, provider = 'deepseek-official', lo
245
246
  // Keep the durable prompt reading visible instead of losing all telemetry.
246
247
  let data;
247
248
  try { data = id ? source?.(id) : undefined; } catch { /* use the TUI's durable projection */ }
248
- const ledger = id && process.env.DSH_HOME ? readMetrics(process.env.DSH_HOME, id) : { rows: [], corrupt: false };
249
+ const ledger = id ? readMetrics(resolveDshHome(), id) : { rows: [], corrupt: false };
249
250
  const events = data?.events ?? [];
250
251
  // Identity alone is not enough: a session event list may be appended to in place.
251
252
  const hit = events.length > 0 ? footerCache.get(events) : undefined;
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { clientRequestSchema } from '@deepseek-ai/dsh-client-connection';
2
3
  import { DesktopTriggerControl, DesktopTriggerManagement } from './desktop-management.mjs';
3
4
  import { registerTriggerTools } from './tools.mjs';
@@ -33,8 +34,7 @@ export async function triggerDesktopRequest(ctx, { control, management }, payloa
33
34
  }
34
35
 
35
36
  export function apply(ctx) {
36
- const home = process.env.DSH_HOME;
37
- if (!home) throw Error('Desktop scheduling requires DSH_HOME');
37
+ const home = resolveDshHome();
38
38
  const control = new DesktopTriggerControl(ctx, { home });
39
39
  const management = new DesktopTriggerManagement({ home, control });
40
40
  const service = { control, management };
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, statSync } from 'node:fs';
2
3
  import { join } from 'node:path';
3
4
  import { Logger } from '@deepseek-ai/cordis';
@@ -106,7 +107,7 @@ export async function collectDoctorEvidence(ctx, { agent, cwd = agent?.session.h
106
107
  } catch (e) { traces.push({ id: item.header.id, error: safe(e.message) }); }
107
108
  finally { await handle?.close(); }
108
109
  }
109
- const home = process.env.DSH_HOME ?? process.env.DSCODE_HOME;
110
+ const home = resolveDshHome();
110
111
  return { cwd: safe(cwd), collectedAt: time(Date.now()),
111
112
  logCoverage: home && existsSync(doctorLogPath(home)) ? L('doctor.logs.new') : L('doctor.logs.none'),
112
113
  logs: home ? recentRuntimeLogs(home, ctx.logger?.buffer ?? []) : [], traces };
@@ -1,3 +1,4 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { runShell } from './shell.mjs';
2
3
  import { VERSION_PATTERN, scheduleUpdate } from './update.mjs';
3
4
  import { readLanguage, t } from '../i18n/messages.mjs';
@@ -101,7 +102,7 @@ export function applyDesktop(ctx) {
101
102
  }
102
103
 
103
104
  function install(ctx, desktop) {
104
- const diagnosticsHome = process.env.DSH_HOME ?? process.env.DSCODE_HOME;
105
+ const diagnosticsHome = resolveDshHome();
105
106
  // A minimal composition (and the unit fixtures) may mount no logger; diagnostics are best-effort.
106
107
  if (diagnosticsHome) ctx.logger?.exporter?.({ levels: { default: 2 }, export: message => recordRuntimeLog(diagnosticsHome, message) });
107
108
  const composition = agent => {