dsh-deeppilot 0.7.1 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/COMPATIBILITY.md CHANGED
@@ -7,7 +7,7 @@ This file separates tested evidence from intended behavior. Passing unit tests d
7
7
  | Component | Baseline | Evidence |
8
8
  |---|---|---|
9
9
  | Node.js | 22 or newer | package engine and CI |
10
- | DSH CLI and Host API | the `0.1.5-rc.*` and `0.1.6-*`/`0.1.6` lines, `0.1.5-rc.1` minimum, for plugin `0.7.x` | plugin source typecheck, unit suite (265 tests) and `tsdown` build run against the `0.1.5-rc.2`, `0.1.6-alpha.1` and `0.1.6-alpha.2` package families; the `deeppilot/report` contribution registered through both the `0.1.6-alpha.1` and the `0.1.6-alpha.2` `TypertRegistry`; bridge and `/phone` protocol tests |
10
+ | DSH CLI and Host API | the `0.1.5-rc.*` and `0.1.6-*`/`0.1.6` lines, `0.1.5-rc.1` minimum, for plugin `0.7.x` | plugin source typecheck, unit suite (301 tests) and `tsdown` build run against the `0.1.5-rc.2`, `0.1.6-alpha.1` and `0.1.6-alpha.2` package families; the `deeppilot/report` contribution registered through both the `0.1.6-alpha.1` and the `0.1.6-alpha.2` `TypertRegistry`; bridge and `/phone` protocol tests |
11
11
  | Host OS | macOS, Linux, Windows on packaged amd64/arm64 helper targets | helper checksums plus user-confirmed Windows/Linux Funnel launch and connection; local LAN validation remains part of alpha testing |
12
12
  | Remote access | Tailscale Funnel, ports 443/8443/10000 | helper and supervisor tests |
13
13
  | iOS | native DeepPilot client, protocol v2 | simulator build and v2 pairing/challenge evidence |
@@ -24,12 +24,18 @@ This file separates tested evidence from intended behavior. Passing unit tests d
24
24
  Older plugin versions used developer-preview DSH builds and remain historical
25
25
  artifacts.
26
26
  - The declared peer range is a disjunction,
27
- `^0.1.5-rc.1 || >=0.1.6-alpha.1 <0.2.0-0`, because npm's prerelease rule only
27
+ `^0.1.5-rc.1 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0`,
28
+ because npm's prerelease rule only
28
29
  lets a prerelease version satisfy a range whose comparators carry a
29
30
  prerelease on the same `major.minor.patch` tuple. Neither `^0.1.5-rc.1` nor
30
- `>=0.1.5-rc.1` alone admits a `0.1.6` prerelease. `tests/compatibility-metadata.test.ts`
31
+ `>=0.1.5-rc.1` alone admits a `0.1.6` prerelease, and the `0.1.7-alpha.*`
32
+ line is likewise rejected by `>=0.1.6-alpha.1 <0.2.0-0` because no
33
+ comparator there carries a prerelease on the `0.1.7` tuple — every new
34
+ prerelease line needs its own disjunct.
35
+ `tests/compatibility-metadata.test.ts`
31
36
  asserts the range admits `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`,
32
- `0.1.6-beta.1` and `0.1.6`, and rejects `0.2.0`.
37
+ `0.1.6-beta.1`, `0.1.6`, `0.1.7-alpha.1` and `0.1.7`,
38
+ and rejects `0.2.0`.
33
39
  - DSH `0.1.6-alpha.1` replaced `agent/session-start` with `agent/created`,
34
40
  deprecated the synchronous `snapshotEvents`/`eventAt`/`ownEvents` history
35
41
  readers, and renamed the PTC packages to the `ptc-runtime` family. The plugin
@@ -51,6 +57,69 @@ This file separates tested evidence from intended behavior. Passing unit tests d
51
57
  The other `alpha.2` Typert change — a contributed schema's materialized
52
58
  `schema` becoming a lazy `create()` factory — does not apply here: this
53
59
  Remote contributes no schemas, which the same test asserts.
60
+ - DSH `0.1.7-alpha.1` reordered the Host `typertGateway.wireStream.open`
61
+ parameters from `(endpoint, payload, signal)` to
62
+ `(endpoint, payload, uplink, peer, signal)`. The reorder is not additive: a
63
+ 3-argument call lands the `AbortSignal` in the `uplink` slot, leaves `signal`
64
+ undefined, and the Host fails every stream inside
65
+ `AbortSignal.any([signal, …])` with
66
+ `TypeError [ERR_INVALID_ARG_TYPE]: The "signals[0]" argument must be an
67
+ instance of AbortSignal`. The resident Client's Connection treats that dead
68
+ generation as a lost connection and prints an endless
69
+ `[connection] connection lost, retry #N` loop with exponential backoff while
70
+ the phone's approval and question round-trips stay broken.
71
+ `openHostStream()` in `src/dsh012-remote-interactions.ts` selects the
72
+ contract from the published arity (`open.length`) and passes `undefined` for
73
+ both `uplink` and `peer` on 0.1.7 — the documented "operator's in-process
74
+ carrier" case, since DeepPilot's carrier owns the Host in process and has no
75
+ Client-to-Host uplink. `tests/dsh012-wire-stream-contract.test.ts` pins the
76
+ argument binding for a 3-arity and a 5-arity Host, and asserts the signal
77
+ lands in each contract's declared slot.
78
+ - DSH `0.1.7-alpha.1` rewrote the settings subsystem: `installSection`,
79
+ `SettingsSectionHooks`, and the client `settingsScope` service are gone,
80
+ section values persist in this plugin own profile config entry, and the
81
+ settings UI refuses writes to fields the schema does not declare
82
+ `.volatile()`. The plugin adapts across both generations: `live()` in
83
+ `src/config.ts` feature-detects the volatile method and, on schemastery
84
+ 3.18.2, writes the same metadata through `extra('volatile', true)` (an
85
+ unconditional `.volatile()` throws at import on ≤ 0.1.6 and kills
86
+ activation); volatile fields are exactly the ones the settings surface
87
+ writes (`enabled`, `local`, `remote`, plus read-time `debug`) while
88
+ `devicesPath`, `historyBufferMax`, and `push` keep remount-on-edit
89
+ semantics; `normalizeOptions` unwraps the `{ get() }`
90
+ references 0.1.7 carries in `apply()` options (a surviving reference fails
91
+ every plain-value comparison the bridge makes), the host re-runs its
92
+ transport reconciles on `loader/volatile-update`, and the client binds
93
+ `ctx.configForms.get('deeppilot')` through `src/client/settings-scope.ts`
94
+ when `settingsScope` is absent. Neither seam is declared in the client
95
+ entry's `inject`: a service name no running host provides keeps the whole
96
+ entry pending (`dsh-deeppilot: pending (waiting for service: settingsScope)`
97
+ on the first 0.1.7 boot), so both are awaited as optional injections, and
98
+ both are read through `ctx.get()` because a Cordis context proxy throws
99
+ `cannot get property "settingsScope" without inject` on an undeclared
100
+ service read; `tests/client-entry.test.ts` activates the real entry against
101
+ a Cordis context shaped like each generation and pins that audit. The same
102
+ release added
103
+ `session.projections`; the bridge feature-detects it via
104
+ `supportsProjections` and refreshes a session projection baseline on open,
105
+ while older hosts keep list-row projection hints. Plugin-manager display
106
+ metadata (`locale/*.json`, `package.json.icon`) and `--dump-config-schema`
107
+ are additive and inert on older hosts; `npm run check:config-schema`
108
+ projects this package Config through the pinned 0.1.7 CLI in CI and pins
109
+ the volatile annotations on the four live fields (devDependency floor:
110
+ `@deepseek-ai/schemastery` 3.18.3, which is also what a profile install
111
+ resolves through the `^3.18.2` peer range).
112
+ - `icon.svg` is a vector recreation of the iOS app icon, built from the
113
+ private app's own Icon Composer sources (`ios/DeepPilot.icon/icon.json`
114
+ plus `Assets/deepseek.svg`) and the in-app composition its
115
+ `AppIconArtwork` applies: a white field, the DeepSeek brand glyph fitted
116
+ with 13% padding, and a `Color(white: 0.25)` → black vertical gradient.
117
+ The app's `.icon` bundle is not distributable through npm — the host
118
+ inlines a single relative SVG, PNG, JPEG, or WebP file under 256 KiB as a
119
+ `data:` URI — so the recreation is the only way to keep the two icons
120
+ visually identical. `tests/package-meta.test.ts` pins the host's reader
121
+ rules; regenerate the SVG from the app sources whenever the app icon
122
+ changes.
54
123
  - DSH `0.1.6-alpha.2` made runtime plugin removal real, through both the
55
124
  Plugin Manager and the client entry reconciler. Every long-lived resource is
56
125
  registered through `ctx.effect`, and the settings-page stylesheet stamps
@@ -70,6 +139,12 @@ This file separates tested evidence from intended behavior. Passing unit tests d
70
139
 
71
140
  - Intel macOS support for the embedded helper;
72
141
  - signed/notarized helper distribution;
142
+ - validation against the `0.1.7-*` package line: the peer range admits
143
+ `0.1.7-alpha.1` and later `0.1.7` releases, and the `wireStream.open`
144
+ reorder that release introduced is adapted and pinned by
145
+ `tests/dsh012-wire-stream-contract.test.ts`, but the unit suite still runs
146
+ against the `0.1.5-rc.1`/`0.1.6-*` package families and no `0.1.7`
147
+ typecheck or full-suite evidence has been produced yet;
73
148
  - repair or migration of persisted history rejected by the Host's own session
74
149
  reader;
75
150
  - every DSH developer-preview revision;
package/README.md CHANGED
@@ -38,16 +38,18 @@ does not require the helper.
38
38
 
39
39
  | Plugin version | Required DSH | How to install |
40
40
  |---|---|---|
41
- | `0.7.x` (current stable, `latest`) | DSH `0.1.5-rc.1` or newer | `dsh plugin --profile web add dsh-deeppilot` |
42
- | `0.6.x` (previous stable) | DSH `0.1.5-rc.1` or newer | `dsh plugin --profile web add dsh-deeppilot@0.6.2` |
43
- | `0.5.x` (legacy) | DSH `0.1.1-rc.2`–`0.1.2-alpha.1` | `dsh plugin --profile web add dsh-deeppilot@0.5.0` |
41
+ | `0.8.x` (current stable, `latest`) | DSH `0.1.5-rc.1` or newer | `dsh plugin --profile web add dsh-deeppilot` |
42
+ | `0.7.x` (previous stable) | DSH `0.1.5-rc.1` or newer | `dsh plugin --profile web add dsh-deeppilot@0.7.1` |
43
+ | `0.6.x` (legacy) | DSH `0.1.5-rc.1` or newer | `dsh plugin --profile web add dsh-deeppilot@0.6.2` |
44
44
 
45
- `0.7.x` is built and typechecked against the DSH
45
+ `0.8.x` is built and typechecked against the DSH
46
46
  [0.1.5-rc.1](https://www.npmjs.com/package/@deepseek-ai/dsh/v/0.1.5-rc.1)
47
- Host and client package family. It uses the Gateway multi-client Remote Events
48
- routing that keeps Web and DeepPilot independently answerable. Earlier plugin
49
- alphas remain historical artifacts; users installing the `alpha` tag for
50
- pre-release testing should update DSH to `0.1.5-rc.1` or newer.
47
+ and [0.1.7-alpha.1](https://www.npmjs.com/package/@deepseek-ai/dsh/v/0.1.7-alpha.1)
48
+ Host and client package families — the config-schema release check projects the
49
+ plugin's Config through the pinned 0.1.7 CLI. It uses the Gateway multi-client
50
+ Remote Events routing that keeps Web and DeepPilot independently answerable.
51
+ Earlier plugin alphas remain historical artifacts; users installing the `alpha`
52
+ tag for pre-release testing should update DSH to `0.1.5-rc.1` or newer.
51
53
 
52
54
  ```sh
53
55
  # DSH 0.1.5-rc.1 or newer (recommended):
@@ -92,15 +94,16 @@ DeepPilot state under `$DSH_HOME/deeppilot/`.
92
94
 
93
95
  ## Publishing (maintainers)
94
96
 
95
- `0.7.x` targets DSH `0.1.5-rc.1`+; `0.6.x` keeps the pre-TLS LAN transport for
96
- the same DSH family during the migration window, and `0.5.x` stays compatible
97
- with DSH `0.1.1-rc.2`–`0.1.2-alpha.1`. Keep them published:
97
+ `0.8.x` targets DSH `0.1.5-rc.1`+ and adapts to the DSH 0.1.7 settings rewrite;
98
+ `0.7.x` keeps the pre-0.1.7 settings API during the migration window, `0.6.x`
99
+ keeps the pre-TLS LAN transport for the same DSH family, and `0.5.x` stays
100
+ compatible with DSH `0.1.1-rc.2`–`0.1.2-alpha.1`. Keep them published:
98
101
 
99
102
  1. Bump `version` in `package.json` and in the root `""` entry of
100
103
  `package-lock.json`, then run `npm test && npm run typecheck && npm run build`
101
104
  and inspect `npm pack --dry-run --json` (the check
102
105
  `tests/compatibility-metadata.test.ts` enforces the peer range and asserts it
103
- admits both the `0.1.5-rc.*` and the `0.1.6-*` lines).
106
+ admits the `0.1.5-rc.*`, the `0.1.6-*`, and the `0.1.7-*` lines).
104
107
  2. Commit the release and push it. `npm publish` runs `prepack` (build) and
105
108
  `prepublishOnly` (test + typecheck) automatically.
106
109
  3. Publish pre-releases without touching `latest`:
@@ -110,7 +113,7 @@ with DSH `0.1.1-rc.2`–`0.1.2-alpha.1`. Keep them published:
110
113
  ```
111
114
 
112
115
  After a successful publish, `npm view dsh-deeppilot dist-tags --json` shows
113
- `"latest": "0.7.x"` and `"alpha": "0.7.x-alpha.y"`. Verify the published
116
+ `"latest": "0.8.x"` and `"alpha": "0.8.x-alpha.y"`. Verify the published
114
117
  package by installing it into a DSH `0.1.5-rc.1` profile before pointing
115
118
  users at it.
116
119
  4. Tag the release commit `vX.Y.Z` and prepare a GitHub Release
package/README.zh-CN.md CHANGED
@@ -35,13 +35,15 @@
35
35
 
36
36
  | 插件版本 | 所需 DSH | 安装命令 |
37
37
  |---|---|---|
38
- | `0.7.x`(当前稳定版,`latest`) | DSH `0.1.5-rc.1` 或更高 | `dsh plugin --profile web add dsh-deeppilot` |
39
- | `0.6.x`(旧版稳定版) | DSH `0.1.5-rc.1` 或更高 | `dsh plugin --profile web add dsh-deeppilot@0.6.2` |
40
- | `0.5.x`(历史版本) | DSH `0.1.1-rc.2`–`0.1.2-alpha.1` | `dsh plugin --profile web add dsh-deeppilot@0.5.0` |
38
+ | `0.8.x`(当前稳定版,`latest`) | DSH `0.1.5-rc.1` 或更高 | `dsh plugin --profile web add dsh-deeppilot` |
39
+ | `0.7.x`(旧版稳定版) | DSH `0.1.5-rc.1` 或更高 | `dsh plugin --profile web add dsh-deeppilot@0.7.1` |
40
+ | `0.6.x`(历史版本) | DSH `0.1.5-rc.1` 或更高 | `dsh plugin --profile web add dsh-deeppilot@0.6.2` |
41
41
 
42
- `0.7.x` 基于 DSH
42
+ `0.8.x` 基于 DSH
43
43
  [0.1.5-rc.1](https://www.npmjs.com/package/@deepseek-ai/dsh/v/0.1.5-rc.1)
44
- 的 Host 与 client 包族构建并完成类型检查。它使用 Gateway 多 Client Remote Events
44
+ 与 [0.1.7-alpha.1](https://www.npmjs.com/package/@deepseek-ai/dsh/v/0.1.7-alpha.1)
45
+ 的 Host 与 client 包族构建并完成类型检查 —— 发布前的 config-schema 检查会通过
46
+ 固定的 0.1.7 CLI 投影本插件的 Config。它使用 Gateway 多 Client Remote Events
45
47
  路由,使 Web 与 DeepPilot 能独立处理同一交互。旧 alpha 仅保留为历史产物;安装
46
48
  `alpha` tag 进行预发布测试的用户应先把 DSH 更新到 `0.1.5-rc.1` 或更高版本。
47
49
 
@@ -80,14 +82,14 @@ dsh plugin --profile web remove dsh-deeppilot
80
82
 
81
83
  ## 发布说明(维护者)
82
84
 
83
- `0.7.x` 面向 DSH `0.1.5-rc.1`+;`0.6.x` 在同一 DSH 版本族上保留 TLS 化之前的
84
- 局域网传输,供迁移期使用;`0.5.x` 保持兼容 DSH
85
- `0.1.1-rc.2`–`0.1.2-alpha.1`,各版本线都要保持发布:
85
+ `0.8.x` 面向 DSH `0.1.5-rc.1`+ 并适配 DSH 0.1.7 的设置系统重写;迁移期内
86
+ `0.7.x` 保留 0.1.7 之前的设置 API,`0.6.x` 在同一 DSH 版本族上保留 TLS 化之前的
87
+ 局域网传输,`0.5.x` 保持兼容 DSH `0.1.1-rc.2`–`0.1.2-alpha.1`,各版本线都要保持发布:
86
88
 
87
89
  1. 同步修改 `package.json` 与 `package-lock.json` 根 `""` 条目中的
88
90
  `version`,然后运行 `npm test && npm run typecheck && npm run build`,
89
91
  并检查 `npm pack --dry-run --json`(`tests/compatibility-metadata.test.ts`
90
- 会强制校验 peer 范围,并断言它同时接纳 `0.1.5-rc.*` 与 `0.1.6-*` 两条线)。
92
+ 会强制校验 peer 范围,并断言它同时接纳 `0.1.5-rc.*`、`0.1.6-*` 与 `0.1.7-*` 三条线)。
91
93
  2. 提交发布并推送。`npm publish` 会自动执行 `prepack`(构建)与
92
94
  `prepublishOnly`(测试 + 类型检查)。
93
95
  3. 发布预发布版本,不要动 `latest`:
@@ -97,7 +99,7 @@ dsh plugin --profile web remove dsh-deeppilot
97
99
  ```
98
100
 
99
101
  发布成功后,`npm view dsh-deeppilot dist-tags --json` 应显示
100
- `"latest": "0.7.x"` 与 `"alpha": "0.7.x-alpha.y"`。向用户推荐前,请先在
102
+ `"latest": "0.8.x"` 与 `"alpha": "0.8.x-alpha.y"`。向用户推荐前,请先在
101
103
  DSH `0.1.5-rc.1` profile 中安装验证发布的包。
102
104
  4. 为发布提交打 `vX.Y.Z` tag,并准备包含英文与简体中文说明的
103
105
  GitHub Release,链接本 README 的发布说明。
package/icon.svg ADDED
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="DeepPilot">
2
+ <defs>
3
+ <linearGradient id="dp-glyph" x1="0" y1="-18.1112" x2="0" y2="52.2563" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0" stop-color="#404040"/>
5
+ <stop offset="1" stop-color="#000000"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="128" height="128" fill="#ffffff"/>
9
+ <g transform="translate(17.5525 17.0725) scale(3.950123)">
10
+ <path fill="url(#dp-glyph)" fill-rule="evenodd" d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"/>
11
+ </g>
12
+ </svg>
package/lib/client.js CHANGED
@@ -4064,6 +4064,74 @@ window.__ModuleLoader__.load({
4064
4064
  }, translateWith(props.t, "update.badge")) : null), diag.length > 0 ? (0, react.createElement)("p", { className: "pbb-diag" + (failed ? " pbb-diagBad" : "") }, translateWith(props.t, "diag.prefix") + diag.join(" | ")) : null);
4065
4065
  }
4066
4066
  //#endregion
4067
+ //#region src/client/settings-scope.ts
4068
+ /** Read one optional settings seam without an inject declaration on this entry. */
4069
+ function readSeam(ctx, name) {
4070
+ if (typeof ctx.get === "function") return ctx.get(name) ?? void 0;
4071
+ return ctx[name];
4072
+ }
4073
+ /**
4074
+ * Deep-unwrap live-update references in a snapshot section.
4075
+ *
4076
+ * If a host parses section values through a volatile-capable schema, leaves
4077
+ * can arrive as `{ get() }` references; the settings page compares plain
4078
+ * values, so unwrap defensively at this single boundary regardless of which
4079
+ * schemastery instance produced the snapshot.
4080
+ */
4081
+ function plainSection(value) {
4082
+ if (value === null || typeof value !== "object") return value;
4083
+ if (typeof value.get === "function") return plainSection(value.get());
4084
+ const plain = {};
4085
+ for (const [key, item] of Object.entries(value)) plain[key] = item !== null && typeof item === "object" ? plainSection(item) : item;
4086
+ return plain;
4087
+ }
4088
+ /**
4089
+ * Adapt one 0.1.7 config form to the settings-scope face.
4090
+ *
4091
+ * The form resolves writes as `Promise<boolean>` (false = the Host rejected or
4092
+ * could not recover the write) where the scope face signals failure by
4093
+ * rejecting — the page's optimistic-write rollback only runs on rejection, so
4094
+ * a `false` must be translated into one.
4095
+ *
4096
+ * @param forms - the 0.1.7 configForms service, if the host provides it.
4097
+ * @param entryId - profile entry id; identical to the legacy namespace here.
4098
+ * @returns the adapted scope, or undefined when the service cannot resolve it.
4099
+ */
4100
+ function adaptConfigForm(forms, entryId) {
4101
+ const form = forms?.get?.(entryId);
4102
+ if (form === void 0) return void 0;
4103
+ const accept = async (pending) => {
4104
+ if (!await pending) throw new Error("host rejected the settings write");
4105
+ };
4106
+ return {
4107
+ getSnapshot: () => {
4108
+ const snapshot = form.getSnapshot();
4109
+ return {
4110
+ status: snapshot.status,
4111
+ value: plainSection(snapshot.value),
4112
+ writable: snapshot.writable
4113
+ };
4114
+ },
4115
+ subscribe: (listener) => form.subscribe(listener),
4116
+ set: (field, value) => accept(form.set(field, value)),
4117
+ unset: (field) => accept(form.unset(field))
4118
+ };
4119
+ }
4120
+ /**
4121
+ * Bind the settings scope for whichever host generation is running.
4122
+ *
4123
+ * Prefers the legacy settingsScope (present ≤ 0.1.6); falls back to the 0.1.7
4124
+ * config form when its service is already provisioned. Safe to call from any
4125
+ * context — including one that declares neither service, which on a Cordis
4126
+ * context is the difference between `undefined` and a thrown
4127
+ * `cannot get property "settingsScope" without inject`. Callers that apply
4128
+ * before `configForms` is provided re-run this inside the optional
4129
+ * `ctx.inject(['configForms'], …)` callback (see client index.ts).
4130
+ */
4131
+ function bindSettingsScope(ctx) {
4132
+ return readSeam(ctx, "settingsScope")?.bind({ namespace: "deeppilot" }) ?? adaptConfigForm(readSeam(ctx, "configForms"), "deeppilot");
4133
+ }
4134
+ //#endregion
4067
4135
  //#region src/client/index.ts
4068
4136
  /** Polls the report remote and owns the page state transitions. */
4069
4137
  var ReportController = class {
@@ -4116,11 +4184,21 @@ window.__ModuleLoader__.load({
4116
4184
  } catch {}
4117
4185
  }
4118
4186
  };
4187
+ /**
4188
+ * Services this client entry cannot start without.
4189
+ *
4190
+ * The settings seam is deliberately NOT here: DSH ≤ 0.1.6 provides
4191
+ * `settingsScope`, 0.1.7 removed it in favor of `configForms`, and a name no
4192
+ * running host provides keeps the whole entry pending forever — the 0.1.7 boot
4193
+ * reported exactly `dsh-deeppilot: pending (waiting for service:
4194
+ * settingsScope)` while every other service resolved and the entry never
4195
+ * applied. Both settings seams are optional injections inside `apply`
4196
+ * instead; see the scope binding below.
4197
+ */
4119
4198
  const inject = [
4120
4199
  "slots",
4121
4200
  "locale",
4122
- "remote",
4123
- "settingsScope"
4201
+ "remote"
4124
4202
  ];
4125
4203
  function apply(ctx) {
4126
4204
  if (typeof document !== "undefined") injectCss();
@@ -4196,7 +4274,10 @@ window.__ModuleLoader__.load({
4196
4274
  status: "loading",
4197
4275
  enabled: true
4198
4276
  });
4199
- const scope = anyCtx.settingsScope?.bind({ namespace: "deeppilot" });
4277
+ let scope = bindSettingsScope(anyCtx);
4278
+ /** Adopters registered before attachScope runs, so a late-bound scope (0.1.7
4279
+ * configForms provisioned after this plugin applies) still subscribes all. */
4280
+ const scopeAdoptions = [];
4200
4281
  const adoptEnabled = () => {
4201
4282
  if (scope === void 0) return;
4202
4283
  const snap = scope.getSnapshot();
@@ -4209,10 +4290,7 @@ window.__ModuleLoader__.load({
4209
4290
  enabled: true
4210
4291
  });
4211
4292
  };
4212
- if (scope !== void 0) {
4213
- scope.subscribe(adoptEnabled);
4214
- adoptEnabled();
4215
- }
4293
+ scopeAdoptions.push(adoptEnabled);
4216
4294
  const lastConfirmedEnabled = () => {
4217
4295
  const snap = scope?.getSnapshot();
4218
4296
  if (snap?.status === "ready" && snap.value !== void 0) return snap.value.enabled !== false;
@@ -4285,10 +4363,7 @@ window.__ModuleLoader__.load({
4285
4363
  });
4286
4364
  }
4287
4365
  };
4288
- if (scope !== void 0) {
4289
- scope.subscribe(adoptLocal);
4290
- adoptLocal();
4291
- }
4366
+ scopeAdoptions.push(adoptLocal);
4292
4367
  const setDeepPilotLocalEnabled = (value) => {
4293
4368
  const previous = lastConfirmedLocalEnabled();
4294
4369
  localEnabledStore.set({
@@ -4363,9 +4438,27 @@ window.__ModuleLoader__.load({
4363
4438
  });
4364
4439
  }
4365
4440
  };
4366
- if (scope !== void 0) {
4367
- scope.subscribe(adoptRemoteEnabled);
4368
- adoptRemoteEnabled();
4441
+ scopeAdoptions.push(adoptRemoteEnabled);
4442
+ const attachScope = () => {
4443
+ if (scope === void 0) return;
4444
+ for (const adopt of scopeAdoptions) {
4445
+ scope.subscribe(adopt);
4446
+ adopt();
4447
+ }
4448
+ };
4449
+ attachScope();
4450
+ if (scope === void 0) {
4451
+ const bindScopeFrom = (sub) => {
4452
+ if (scope !== void 0) return;
4453
+ scope = bindSettingsScope(sub);
4454
+ attachScope();
4455
+ };
4456
+ ctx.inject(["settingsScope"], (sub) => {
4457
+ bindScopeFrom(sub);
4458
+ });
4459
+ ctx.inject(["configForms"], (sub) => {
4460
+ bindScopeFrom(sub);
4461
+ });
4369
4462
  }
4370
4463
  const setDeepPilotRemoteEnabled = (value) => {
4371
4464
  const previous = lastConfirmedRemoteEnabled();