dsh-deeppilot 0.9.3 → 0.9.5
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 +94 -27
- package/PRIVACY.md +2 -2
- package/README.md +26 -33
- package/README.zh-CN.md +19 -24
- package/SECURITY.md +2 -1
- package/bin/SHA256SUMS +6 -6
- package/bin/darwin-amd64/dsh-deeppilot-tunnel +0 -0
- package/bin/darwin-arm64/dsh-deeppilot-tunnel +0 -0
- package/bin/linux-amd64/dsh-deeppilot-tunnel +0 -0
- package/bin/linux-arm64/dsh-deeppilot-tunnel +0 -0
- package/bin/windows-amd64/dsh-deeppilot-tunnel.exe +0 -0
- package/bin/windows-arm64/dsh-deeppilot-tunnel.exe +0 -0
- package/cordis.patch.yml +1 -1
- package/docs/DEVICE_REGISTRY_RECOVERY.md +87 -0
- package/docs/DEVICE_REGISTRY_RECOVERY.zh-CN.md +73 -0
- package/docs/DSH_RELEASE_MEMORY.md +697 -27
- package/docs/NPM_PUBLISHING.md +83 -0
- package/docs/SECURITY_ROADMAP.md +9 -4
- package/docs/SECURITY_ROADMAP.zh-CN.md +9 -4
- package/lib/client.js +5 -5
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +64 -13
- package/lib/index.js.map +1 -1
- package/locale/en.json +1 -1
- package/package.json +17 -14
|
@@ -5,30 +5,204 @@
|
|
|
5
5
|
|
|
6
6
|
## Current decision
|
|
7
7
|
|
|
8
|
-
- **Audited release:** `dsh-v0.2.1-alpha.
|
|
9
|
-
<https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.
|
|
10
|
-
published 2026-10-
|
|
11
|
-
`latest`/`next` still point at `0.2.0-rc.2
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`
|
|
28
|
-
|
|
8
|
+
- **Audited release:** `dsh-v0.2.1-alpha.3` (official release page:
|
|
9
|
+
<https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.3>),
|
|
10
|
+
published to npm 2026-10-14T13:32:15Z on the `alpha` dist-tag. It is **not**
|
|
11
|
+
`latest`; `latest`/`next` still point at `0.2.0-rc.2`. Baseline commit
|
|
12
|
+
`2fbf4359b82fb94c03dcce0b831660d87f12ea5c`, compared against alpha.2's
|
|
13
|
+
`d743267388641bc76f17c45ce8b4c231aed1d32c` (**852 commits, 3311 changed files,
|
|
14
|
+
120,978 insertions, 16,632 deletions**).
|
|
15
|
+
- **Comparison baseline:** `dsh-v0.2.1-alpha.2` (the previous audited tag).
|
|
16
|
+
- **Plugin runtime verdict: four confirmed breaks with one root cause — the
|
|
17
|
+
alpha.3 Peer model — and the resident interaction client does not start.**
|
|
18
|
+
**Fixed in this audit; see "The fix" below.**
|
|
19
|
+
This release makes every in-process carrier name a `PeerScope`
|
|
20
|
+
(`principal` / `signal` / `interactions`) explicitly; nothing defaults to the
|
|
21
|
+
operator any more. The four landing points, each with its own evidence:
|
|
22
|
+
|
|
23
|
+
| # | Upstream change | Plugin seam | Evidence |
|
|
24
|
+
| --- | --- | --- | --- |
|
|
25
|
+
| 1 | `TypertGatewayWireStream.open`'s 4th parameter `peer` is required; `assertPeerLive()` reads `peer.signal.aborted` before anything else | `src/dsh-remote-interactions.ts:180` passes `undefined, undefined` | Reproduced against the published bundles: alpha.2 returns normally, alpha.3 throws `TypeError: Cannot read properties of undefined (reading 'signal')` |
|
|
26
|
+
| 2 | `$events` additionally requires `peer.interactions === 'answer'` (`requireInteractions`) | same call | Reproduced: a peer with `interactions: 'none'` gets `gateway/interactions-forbidden`; a full owner/answer peer passes on **both** versions |
|
|
27
|
+
| 3 | `ConnectionFetchHandler.fetch(request, peer)` now takes a required Peer | `src/dsh-remote-interactions.ts:204` calls `fetchHandler.fetch(request)` | `lib/types/rpc.d.ts` declares `peer: PeerScope` required, "never defaulted" |
|
|
28
|
+
| 4 | `./client` gained `node`/`browser` conditions; Node now resolves `lib/client.node.js`, which carries **no** `window.__ModuleLoader__.load` banner | `loadClientFaces()` (`src/dsh-remote-interactions.ts:117`) builds its module table from that banner | `grep -c __ModuleLoader__` is 0 in `client.node.js`, 1 in `client.browser.js` and 1 in alpha.2's `client.js` |
|
|
29
|
+
|
|
30
|
+
A fifth, latent one: `installConnection` derives
|
|
31
|
+
`interactions ?? (location === undefined ? 'none' : 'answer')`, and the
|
|
32
|
+
plugin's Node context has no `location`, so `apply(ctx)` alone always selects
|
|
33
|
+
`'none'`. Passing an explicit Peer is not enough by itself — the install call
|
|
34
|
+
must name `interactions: 'answer'` too.
|
|
35
|
+
- **Failure mode is degradation, not a crash — but only partly contained.**
|
|
36
|
+
`src/dsh-api-proxy.ts:316` wraps `startDshRemoteInteractions` in `try/catch`
|
|
37
|
+
and logs `[deeppilot] DSH Remote interaction client unavailable`. Breaks 3 and
|
|
38
|
+
4 surface while the resident client is being assembled, so they land in that
|
|
39
|
+
catch. Whether break 1/2 (the Gateway opens its Remote Events generation
|
|
40
|
+
asynchronously) also lands there is **not confirmed** — an uncaught rejection
|
|
41
|
+
there would be fatal under Node's default `--unhandled-rejections=throw`.
|
|
42
|
+
The phone loses approval/question delivery either way.
|
|
43
|
+
- **Fix direction (verified compatible with both hosted versions):** build one
|
|
44
|
+
owner/answer Peer (`new Peer(ownerCtx, { principal: { kind: 'owner' },
|
|
45
|
+
interactions: 'answer' })`, falling back to `connection.operator` on alpha.2)
|
|
46
|
+
and name it in every carrier call — alpha.2 ignores a second `fetch` argument
|
|
47
|
+
and defaults an absent `open` peer to the operator, so one call form serves
|
|
48
|
+
both. `loadClientFaces` additionally has to accept the Node bundle's named
|
|
49
|
+
exports when the banner did not fire; both shapes export `apply`/`inject`
|
|
50
|
+
(`installConnection`/`transportError` are extra on Node).
|
|
51
|
+
- **Install verdict: the peer range now names this release explicitly.** It moved
|
|
52
|
+
from the open-ended `>=0.2.0-rc.2 <0.3.0-0` to `0.2.0-rc.2 || 0.2.1-alpha.3`
|
|
53
|
+
across all ten `@deepseek-ai/dsh*` peers, and the two vendor peers widened to
|
|
54
|
+
`~4.0.4 || ~4.0.5-alpha.1` and `~3.18.4 || ~3.18.5-alpha.1`. Because DSH
|
|
55
|
+
evaluates that range on every start, the next DSH release deactivates this
|
|
56
|
+
plugin until the list is widened deliberately — see `docs/决策记录.md`
|
|
57
|
+
(章节一, 条目2), which records this as a reversal of the 2026-09-29 policy.
|
|
58
|
+
The published CLI composed the plugin's patch cleanly before the change (see
|
|
59
|
+
the 2026-10-14 entry). The plugin's own `lib/client.js` shape was not
|
|
60
|
+
re-verified against the alpha.3 client-modules scanner.
|
|
61
|
+
- **Protocol verdict: no phone-protocol change.** Protocol v2, pairing state,
|
|
62
|
+
and device records are untouched by this release; no phone needs to re-pair.
|
|
63
|
+
- **Two host-side migrations this machine must apply** (independent of the
|
|
64
|
+
plugin): `~/.dsh/profiles/web/cordis.patch.yml` carries
|
|
65
|
+
`llm-deepseek-account.config.models: []`, and alpha.3 throws on any residual
|
|
66
|
+
`models` key — **including an empty array** (`resolveAccountOptions`). Also
|
|
67
|
+
`dsh --profile <name> --from-default-profile <template>` now *replaces* an
|
|
68
|
+
existing profile directory (it deletes the old `node_modules` and
|
|
69
|
+
`cordis.patch.yml`); the audit procedure recorded below used that flag.
|
|
70
|
+
- **Capability gaps, not regressions:** the `contextBreakdown` projection was
|
|
71
|
+
reshaped (`nodes[]` → `systems[]` + `surfaceTokens` + `claim`, stateVersion
|
|
72
|
+
5→6) and the plugin forwards it by key, so the phone may render empty values;
|
|
73
|
+
the model catalog shrank (account routes return only the version-gated
|
|
74
|
+
`offered` subset, pi-ai routes without a resolvable key return `[]`); and
|
|
75
|
+
`CLIENT_UPGRADE_REQUIRED` / `MODEL_UNSUPPORTED` are new codes with no mapping
|
|
76
|
+
in `src/wire-errors.ts` yet.
|
|
77
|
+
|
|
78
|
+
### Previous decision — `dsh-v0.2.1-alpha.2`
|
|
79
|
+
|
|
80
|
+
- **Audited release:** `dsh-v0.2.1-alpha.2` (official release page:
|
|
81
|
+
<https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.2>),
|
|
82
|
+
published to npm 2026-10-09T08:18:00Z on the `alpha` dist-tag. It is **not**
|
|
83
|
+
`latest`; `latest`/`next` still point at `0.2.0-rc.2`.
|
|
84
|
+
- **Comparison baseline:** `dsh-v0.2.1-alpha.1` (the previous audited tag).
|
|
85
|
+
- **Plugin runtime verdict: one confirmed break, fixed in this audit.**
|
|
86
|
+
`SessionController.projections` now requires its cancellation signal — the
|
|
87
|
+
published implementation calls `signal.throwIfAborted()` as the first
|
|
88
|
+
statement of its `try` and reads `signal.aborted` in its `catch`, while the
|
|
89
|
+
adapter called it with no signal at all, so every projection baseline read
|
|
90
|
+
failed with a `TypeError` instead of returning values. The adapter now passes
|
|
91
|
+
a signal and its hand-written mirror declares `signal` required. See "The one
|
|
92
|
+
confirmed break" in the 2026-10-09 entry for the reproduction, the fix, and
|
|
93
|
+
the regression test that pins it.
|
|
94
|
+
- **What is unaffected:** `list`, `inspect(sessionId)` (its signal is still
|
|
95
|
+
optional upstream), `create`, `fork`, `rename`, `cancel`, `attachment`,
|
|
96
|
+
`prompt`, `selectModel`, `modelCatalog`, the workspace controller, the Gateway
|
|
97
|
+
wire-stream seam the resident approval/question client depends on, the shared
|
|
98
|
+
Fetch carrier, and every peer package's exported runtime surface.
|
|
99
|
+
- **Install verdict:** the peer range `>=0.2.0-rc.2 <0.3.0-0` admits
|
|
100
|
+
`0.2.1-alpha.2` under `semver.satisfies(..., { includePrerelease: true })`,
|
|
101
|
+
which is how DSH's `evaluatePluginCompatibility` gates activation; npm's
|
|
102
|
+
**default** prerelease handling still reports peer warnings for
|
|
103
|
+
`@deepseek-ai/dsh`, `cordis`, and `schemastery`. Composing a real Web profile
|
|
104
|
+
with this plugin's `cordis.patch.yml` on the published CLI adds the plugin's
|
|
105
|
+
three rows plus its own config schema and no plugin-attributed diagnostic; the
|
|
106
|
+
four base-profile schema errors it prints reproduce identically without the
|
|
107
|
+
patch.
|
|
29
108
|
- **Protocol verdict:** no required DeepPilot phone-protocol break. Protocol v2,
|
|
30
109
|
pairing state, and device records are unchanged; already-paired phones need no
|
|
31
110
|
action.
|
|
111
|
+
- **Capability gaps, not regressions:** the new `workingDirectory` projection,
|
|
112
|
+
the new `formatStatus` list field, the `mode: 'external'` subagent catalog, and
|
|
113
|
+
the `session/migration-required` fork refusal are all things this plugin does
|
|
114
|
+
not consume, and none of them change a call it already makes.
|
|
115
|
+
|
|
116
|
+
### Plugin release verdicts
|
|
117
|
+
|
|
118
|
+
- **Release candidate (`0.9.5`, 2026-10-15):** carries the alpha.3 Peer/client
|
|
119
|
+
compatibility repair, the exact two-host peer policy, cross-language auth
|
|
120
|
+
vectors and recovery guidance, and the manual OIDC/provenance workflow.
|
|
121
|
+
The package and both lockfile version entries are `0.9.5`; the apply baseline
|
|
122
|
+
records that version. Clean install, 562 passing unit tests (zero failures,
|
|
123
|
+
one existing TODO), typecheck, build, the real pinned-host schema loader,
|
|
124
|
+
Go tests, all six helper checksums, and package inspection pass locally.
|
|
125
|
+
The altered-challenge test now mutates the challenge from its own socket,
|
|
126
|
+
eliminating the timestamp collision, and passes 20 consecutive focused runs.
|
|
127
|
+
Registry/provenance and clean published-profile checks remain release gates;
|
|
128
|
+
no physical-iPhone validation is claimed. Notes: `docs/RELEASE_NOTES-0.9.5.md`.
|
|
129
|
+
|
|
130
|
+
- **Release verdict (`0.9.4`, published 2026-10-10, tag `v0.9.4` @ `e2f77af`):**
|
|
131
|
+
carries the `projections` adapter fix and the Automation tasks documentation
|
|
132
|
+
correction described in the 2026-10-09 entry below. `package.json`, both root
|
|
133
|
+
`package-lock.json` version entries, and `tests/apply-baseline.snapshot.json`
|
|
134
|
+
are on `0.9.4`, and `lib/` is regenerated. The full checklist passed locally:
|
|
135
|
+
`npm ci`, 538 unit tests, typecheck, build, the config-schema check against a
|
|
136
|
+
real `0.2.0-rc.2` CLI, Go helper tests, helper checksums, and
|
|
137
|
+
`npm pack --dry-run` (65 files, 50,916,470 bytes packed). The fix was also
|
|
138
|
+
verified on a live `0.2.1-alpha.2` host against the published `0.9.3` as an
|
|
139
|
+
A/B control (25 pass / 0 fail either way, with the `sessions.projections
|
|
140
|
+
failed: internal` diagnostic present only on `0.9.3`).
|
|
141
|
+
- **Registry verification after publishing.** `dist-tags.latest` moved off
|
|
142
|
+
`0.9.3`; `dist.shasum` `85b3c71a802e45e42f5fb1b23444ed2e396d39dc` is
|
|
143
|
+
byte-identical to the local `npm pack` output at the tag; 65 files,
|
|
144
|
+
`dist.unpackedSize` 130,789,604. **`gitHead` is `e2f77af`, the tag commit** —
|
|
145
|
+
the 0.9.3 ordering deviation did not repeat, because the release notes and
|
|
146
|
+
every other documentation change were committed before the pack. The publish
|
|
147
|
+
itself needed the maintainer's one-time password, as in 0.9.3; everything up
|
|
148
|
+
to and after it was done here.
|
|
149
|
+
- **Clean-profile verification.** A fresh isolated home, built from the shipped
|
|
150
|
+
Web template and installed with the published `0.2.1-alpha.2` CLI
|
|
151
|
+
(`dsh plugin --profile verify add dsh-deeppilot@0.9.4`), composed a real
|
|
152
|
+
profile: `--dump-config` printed the plugin's three rows
|
|
153
|
+
(`deeppilot-directory-picker-browse`, `…-browse-client`, `deeppilot`) with no
|
|
154
|
+
missing-module or load error, the installed package's six helper checksums
|
|
155
|
+
verified, every helper binary embedded `go1.26.9`, and the installed
|
|
156
|
+
`lib/index.js` carried the projections fix. Only then was the GitHub Release
|
|
157
|
+
taken out of draft and marked Latest.
|
|
158
|
+
- **The release also had to fix the helper's toolchain and its refresh
|
|
159
|
+
workflow.** CI's `govulncheck` job reported 11 reachable Go standard-library
|
|
160
|
+
vulnerabilities for the pinned `go 1.26.6`; this is time-triggered, not
|
|
161
|
+
caused by the release — `0.9.3`'s CI was green on 2026-10-05 with the same
|
|
162
|
+
helper source. `helper/go.mod` now pins `go 1.26.9`, and the six `bin/`
|
|
163
|
+
binaries were rebuilt with it. That rebuild only became correct after fixing
|
|
164
|
+
`.github/workflows/build-helpers.yml`: each build job uploaded the whole
|
|
165
|
+
`bin/` tree, so every artifact also carried the checked-out copies of the
|
|
166
|
+
targets its runner never rebuilt, and `merge-multiple` let those stale files
|
|
167
|
+
overwrite the fresh ones (the first refresh PR mixed two toolchains — only
|
|
168
|
+
darwin-arm64 embedded `go1.26.9`). The workflow now uploads only the
|
|
169
|
+
per-matrix target globs; PR #19 then carried all six at `go1.26.9` and was
|
|
170
|
+
merged as `f0f243b`.
|
|
171
|
+
- **Checklist environment notes for the next release.**
|
|
172
|
+
(a) `~/.npm/_cacache` contains root-owned files on this machine, so `npm view`
|
|
173
|
+
and `npm pack` fail with `EPERM` unless `npm_config_cache` points at a
|
|
174
|
+
writable cache — the run above used the workspace audit cache.
|
|
175
|
+
(b) Go's default build cache lives outside the workspace and the file sandbox
|
|
176
|
+
blocks it (`open .../Library/Caches/go-build/...: operation not permitted`,
|
|
177
|
+
reported as `[setup failed]`); point `GOCACHE` at a workspace directory.
|
|
178
|
+
(c) `gh` writes its run-log cache under `~/.cache/gh` and fails the same way;
|
|
179
|
+
point `XDG_CACHE_HOME` at a workspace directory.
|
|
180
|
+
None of the three is a repository defect.
|
|
181
|
+
- **Release verdict (`0.9.3`, published 2026-10-05, tag `v0.9.3` @ `b85869b`):**
|
|
182
|
+
shipped to `latest`. Verified on the registry: version present, `dist-tags.latest`
|
|
183
|
+
moved off `0.9.2`, 65 files, `dist.shasum` `90f0c665c395a7043eb3689a291cf28c88bc39ab`
|
|
184
|
+
byte-identical to the locally verified `npm pack` output, and a clean install plus
|
|
185
|
+
a real `dsh --profile web --dump-config` compose (188 entries) with the plugin's
|
|
186
|
+
three `cordis.patch.yml` entries present and no missing-module error. Ships
|
|
187
|
+
issue #24 (`100.64.0.0/10` becomes a selectable pairing target) and the Windows
|
|
188
|
+
journal `fsync` tolerance fix. Protocol v2 unchanged; paired phones need no action.
|
|
189
|
+
- **`gitHead` deviation, recorded rather than hidden.** The registry records
|
|
190
|
+
`gitHead` `fbda6a5`, the docs-only commit that added `RELEASE_NOTES-0.9.3.md`,
|
|
191
|
+
while tag `v0.9.3` points at `b85869b`. The cause is ordering, not content:
|
|
192
|
+
the tarball was packed before that docs commit, and npm stamps `gitHead`
|
|
193
|
+
from HEAD at publish time. The shipped bytes are provably `b85869b`'s tree —
|
|
194
|
+
the published `dist.shasum` equals the local `npm pack` output and the tarball
|
|
195
|
+
does not contain `RELEASE_NOTES-0.9.3.md`. `fbda6a5` is `b85869b`'s child and
|
|
196
|
+
changes no packaged file. Versions are immutable on npm, so this cannot be
|
|
197
|
+
corrected retroactively; **do it differently next time: commit every doc file
|
|
198
|
+
before packing, and pack after the last commit.** The 0.9.2 entry below had
|
|
199
|
+
`gitHead` matching the tag because no commit landed between pack and publish.
|
|
200
|
+
- **Install caveat users will hit — DSH's release-age policy.** Confirmed again
|
|
201
|
+
while verifying this release: `dsh plugin add --profile web` writes a
|
|
202
|
+
`minimumReleaseAgeExclude` entry in `pnpm-workspace.yaml` and an explicit
|
|
203
|
+
`dsh-deeppilot@0.9.3` installs the new version. Expect the unversioned form to
|
|
204
|
+
resolve to a slightly older release for a while; this is expected DSH
|
|
205
|
+
behaviour, not a publishing failure.
|
|
32
206
|
- **Release verdict (`0.9.2`, published 2026-10-05, tag `v0.9.2` @ `4b43671`):**
|
|
33
207
|
shipped to `latest`. Verified on the registry: version present, `dist-tags.latest`
|
|
34
208
|
moved off `0.9.1`, `gitHead` `4b436715` matches the tag and HEAD, 65 files,
|
|
@@ -56,15 +230,47 @@
|
|
|
56
230
|
also `optional` in `peerDependenciesMeta`. Confirm installability by
|
|
57
231
|
composing a real profile, not by iterating the peer table.
|
|
58
232
|
- **Two tracked follow-ups:**
|
|
59
|
-
1.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
2.
|
|
233
|
+
1. ~~Projection signal.~~ **Resolved 2026-10-10:** the adapter passes
|
|
234
|
+
`new AbortController().signal` to `session.projections` and the mirror
|
|
235
|
+
declares `signal` required, so the compiler enforces it. Covered by a new
|
|
236
|
+
regression test that fails against the previous call form. 538 unit tests,
|
|
237
|
+
typecheck, and build pass with regenerated `lib/`.
|
|
238
|
+
2. **Automation tasks documentation: public docs fixed, iOS copy open.**
|
|
239
|
+
Automation was a short-lived optional bundle: DSH 0.2.0 moved it out of the
|
|
240
|
+
shipped Web composition, and the following release retired
|
|
241
|
+
`@deepseek-ai/dsh-experimental-schedule-bundle` again — `dsh-web-app` now
|
|
242
|
+
mounts `schedule` and `ui-schedule` in every Web profile. `COMPATIBILITY.md`,
|
|
243
|
+
`PROTOCOL.md`, `docs/DEEPPILOT_FEATURE_PLAN.md`, and the schedule-service
|
|
244
|
+
comment in `src/dsh-api-proxy.ts` now state that; the private app still
|
|
245
|
+
ships `error.scheduleUnsupported` and `schedule.unsupported` copy telling
|
|
246
|
+
the user to enable it (`app-private/ios/L10nFragments/schedule.json` and the
|
|
247
|
+
generated `zh-Hans.lproj` / `en.lproj` string tables). **iOS copy still
|
|
248
|
+
open** — it belongs to the private repository and to its own release flow.
|
|
249
|
+
3. ~~`subagent_session` is unmapped.~~ **Resolved 2026-10-03:** mapped to
|
|
65
250
|
`E_PROTOCOL` in `src/wire-errors.ts` and added to the frozen expectation in
|
|
66
251
|
`tests/wire-errors.test.ts`. 525 unit tests, typecheck, and build pass.
|
|
67
252
|
|
|
253
|
+
### Previous decision — `dsh-v0.2.1-alpha.1`
|
|
254
|
+
|
|
255
|
+
- **Audited release:** `dsh-v0.2.1-alpha.1`, published 2026-10-03 (npm
|
|
256
|
+
`alpha` dist-tag, also not `latest`). Comparison baseline was
|
|
257
|
+
`dsh-v0.2.0-rc.2`.
|
|
258
|
+
- **Plugin runtime verdict:** no breaking change in the DSH APIs DeepPilot calls.
|
|
259
|
+
All 12 peer packages had **zero** changed `src/` files; each changed only its
|
|
260
|
+
`package.json` version field and its READMEs. Full table in the 2026-10-03
|
|
261
|
+
entry below.
|
|
262
|
+
- **Nothing was changed to support that release.** `package.json`,
|
|
263
|
+
`package-lock.json`, generated `lib/`, `COMPATIBILITY.md`, and `PROTOCOL.md`
|
|
264
|
+
stayed on the `0.2.0-rc.2` baseline — no version bump, no dependency install,
|
|
265
|
+
no `lib/` regeneration. The same held for its source diff; the alpha.2 audit
|
|
266
|
+
that followed then produced one adapter fix (see the 2026-10-09 entry).
|
|
267
|
+
- **Peer range:** `>=0.2.0-rc.2 <0.3.0-0` admitted `0.2.1-alpha.1` through
|
|
268
|
+
`semver.satisfies(..., { includePrerelease: true })`; the `~4.0.4` / `~3.18.4`
|
|
269
|
+
vendor ranges admitted `4.0.5-alpha.1` / `3.18.5-alpha.1` the same way, and
|
|
270
|
+
npm's default prerelease rules reported warnings for those three.
|
|
271
|
+
- **Protocol verdict:** no required phone-protocol break; protocol v2, pairing
|
|
272
|
+
state, and device records unchanged.
|
|
273
|
+
|
|
68
274
|
### Previous decision — `dsh-v0.2.0-rc.2`
|
|
69
275
|
|
|
70
276
|
- **Audited release:** `dsh-v0.2.0-rc.2` (official release page:
|
|
@@ -100,6 +306,470 @@
|
|
|
100
306
|
bundle" below. The plugin needs no code change; the phone's copy now points
|
|
101
307
|
at the bundle.
|
|
102
308
|
|
|
309
|
+
## 2026-10-14 — audit of `dsh-v0.2.1-alpha.3`
|
|
310
|
+
|
|
311
|
+
### Scope and method
|
|
312
|
+
|
|
313
|
+
Compared `dsh-v0.2.1-alpha.2...dsh-v0.2.1-alpha.3` (**852 commits, 3311 changed
|
|
314
|
+
files, 120,978 insertions, 16,632 deletions**) by fetching the new tag into the
|
|
315
|
+
existing blobless clone (`.audit-dsh-alpha2`, whose HEAD was already the alpha.2
|
|
316
|
+
audit's target SHA `d7432673`), then `git diff --name-status` and per-path
|
|
317
|
+
content diffs into `.audit-alpha3/diffs/`. Four parallel read-only passes
|
|
318
|
+
covered `packages/api` + `apps/cli` + `packages/typert` + `packages/settings` +
|
|
319
|
+
`vendor`, `packages/client/{connection,locale,ui-settings,ui-slots}` +
|
|
320
|
+
`packages/boot` + `packages/bundle`, `packages/{credentials,subagent,session,
|
|
321
|
+
schedule}`, and `packages/{core,host,skill,llm}`. All **14** upgrade guides
|
|
322
|
+
added by this release (under `docs/upgrade-guide/v0.2.0-rc.2/`,
|
|
323
|
+
`v0.2.1-alpha.1/`, `v0.2.1-alpha.2/`) were read in Chinese in full.
|
|
324
|
+
|
|
325
|
+
The alpha.2 audit's lesson — judge the seams reached through `ctx.get`, not just
|
|
326
|
+
the peer list — is what caught this one: the peer list is *not* where the breaks
|
|
327
|
+
are. `@deepseek-ai/dsh-api-remote-access` is new, `packages/schedule/schedule`
|
|
328
|
+
has **zero** changed source files, and the peer packages' own exported surfaces
|
|
329
|
+
moved far less than the carrier contracts around them.
|
|
330
|
+
|
|
331
|
+
### The four confirmed breaks
|
|
332
|
+
|
|
333
|
+
Every one is the same root cause: alpha.3 makes the Peer explicit. `PeerScope`
|
|
334
|
+
(`@deepseek-ai/dsh-typert-protocol`) gained required `principal`, `signal`, and
|
|
335
|
+
`interactions`; `client/connection` added a `Peer` class and deleted
|
|
336
|
+
`OperatorPeer`; the Gateway deleted `operatorPeer()` and its
|
|
337
|
+
`request.peer ?? this.operatorPeer()` fallback.
|
|
338
|
+
|
|
339
|
+
Reproduction used the **published** bundles, the same technique as the alpha.2
|
|
340
|
+
`projections` break, with the plugin's exact argument list
|
|
341
|
+
(`open('$events', payload, undefined, peer, signal)`):
|
|
342
|
+
|
|
343
|
+
```sh
|
|
344
|
+
# .audit-alpha3/repro-peer-contract.mjs, against
|
|
345
|
+
# alpha.2: ~/.npm/_npx/b5c6312874079fe8/node_modules/@deepseek-ai/dsh-api-gateway/lib/index.js
|
|
346
|
+
# alpha.3: .audit-alpha3/cli-install/node_modules/@deepseek-ai/dsh-api-gateway/lib/index.js
|
|
347
|
+
# A. peer = undefined (what the plugin sends today)
|
|
348
|
+
alpha.2: 未抛错
|
|
349
|
+
alpha.3: 抛错 TypeError: Cannot read properties of undefined (reading 'signal')
|
|
350
|
+
# B. peer = { principal: owner, signal, interactions: 'none' }
|
|
351
|
+
alpha.2: 未抛错
|
|
352
|
+
alpha.3: 抛错 TypertGatewayError: $events: forwarded Remote events require interactions=answer
|
|
353
|
+
# C. peer = { principal: owner, signal, interactions: 'answer' }
|
|
354
|
+
alpha.2: 未抛错
|
|
355
|
+
alpha.3: 未抛错
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Break 4 was settled by reading the shipped artifacts rather than by inference:
|
|
359
|
+
`@deepseek-ai/dsh-client-connection`'s `./client` now maps `node` →
|
|
360
|
+
`lib/client.node.js`, and that file contains **zero** `__ModuleLoader__`
|
|
361
|
+
occurrences while `client.browser.js` and alpha.2's `client.js` each contain
|
|
362
|
+
one. `loadClientFaces` builds its module table *only* from that banner, so on
|
|
363
|
+
Node the table stays empty. Both shapes export `apply` and `inject`, which is
|
|
364
|
+
what makes a banner-optional fallback possible.
|
|
365
|
+
|
|
366
|
+
### The fix
|
|
367
|
+
|
|
368
|
+
One call form serves both audited hosts, because alpha.2 ignores a second
|
|
369
|
+
`fetch` argument and defaults an absent `open` peer to the operator Peer it
|
|
370
|
+
holds:
|
|
371
|
+
|
|
372
|
+
- `src/dsh-remote-interactions.ts` mints one Peer through
|
|
373
|
+
`connection.createPeer({ principal: { kind: 'owner' }, interactions: 'answer' })`
|
|
374
|
+
and falls back to `connection.operator` on a Host without that entry point.
|
|
375
|
+
The same Peer is named on `fetch` and on `wireStream.open`.
|
|
376
|
+
- The Connection install prefers the explicit
|
|
377
|
+
`installConnection(ctx, { transport, interactions: 'answer' })`, because
|
|
378
|
+
`apply(ctx)` cannot name the participation and therefore always selects
|
|
379
|
+
`'none'` for this caller; `apply` remains the fallback for a host that predates
|
|
380
|
+
the installer.
|
|
381
|
+
- `loadClientFaces` accepts a Node bundle's named exports when the browser
|
|
382
|
+
banner did not register them. A banner registration still wins, because the
|
|
383
|
+
bundle's own internal `require()` resolves against it.
|
|
384
|
+
|
|
385
|
+
Six regression tests in `tests/dsh-wire-stream-contract.test.ts` pin the
|
|
386
|
+
contract: the operator fallback, the alpha.3 `createPeer` request shape and the
|
|
387
|
+
Peer reaching `wireStream.open`, both installer branches, and both module-table
|
|
388
|
+
branches. The file fails against the previous call form — verified by restoring
|
|
389
|
+
the source and re-running (1 failure) — and passes after the fix.
|
|
390
|
+
|
|
391
|
+
### Peer range narrowed to the two audited hosts
|
|
392
|
+
|
|
393
|
+
By explicit user direction the DSH peer range stopped being open-ended:
|
|
394
|
+
`>=0.2.0-rc.2 <0.3.0-0` became `0.2.0-rc.2 || 0.2.1-alpha.3` across all ten
|
|
395
|
+
`@deepseek-ai/dsh*` peers, with `@deepseek-ai/cordis` widened to
|
|
396
|
+
`~4.0.4 || ~4.0.5-alpha.1` and `@deepseek-ai/schemastery` to
|
|
397
|
+
`~3.18.4 || ~3.18.5-alpha.1`. The two vendor peers are outside DSH's own peer
|
|
398
|
+
check — it reads only `@deepseek-ai/dsh` and `@deepseek-ai/dsh-*` — so their
|
|
399
|
+
ranges matter only to npm's install-time resolution, which runs without
|
|
400
|
+
`includePrerelease`; naming both vendored prereleases keeps a clean install on
|
|
401
|
+
either host. `devDependencies` stay pinned to `0.2.0-rc.2`.
|
|
402
|
+
|
|
403
|
+
The consequence is deliberate: `evaluatePluginCompatibility` runs before a
|
|
404
|
+
profile is composed on every start, so the next DSH release will refuse to
|
|
405
|
+
activate this plugin until the list is widened by an audit. That reversal of the
|
|
406
|
+
2026-09-29 policy is recorded in `docs/决策记录.md` (章节一, 条目2).
|
|
407
|
+
`tests/compatibility-metadata.test.ts` now asserts both admissions, fifteen
|
|
408
|
+
rejections, and the two vendor ranges.
|
|
409
|
+
|
|
410
|
+
### Host-side changes with no plugin impact
|
|
411
|
+
|
|
412
|
+
`packages/schedule/schedule/**` has no changed source; the schedule error-code
|
|
413
|
+
table is identical, so `src/wire-errors.ts`'s twelve mappings (including
|
|
414
|
+
`subagent_session → E_PROTOCOL`) stay correct. No session-log format migration
|
|
415
|
+
exists — `session-format*`, `v0-to-v1`…`v3-to-v4`, `session-persistence`,
|
|
416
|
+
`session-projection*`, `session-log-deepseek` changed only their version and
|
|
417
|
+
locale files — so phone history rendering is unaffected. The subagent descriptor
|
|
418
|
+
is still version 3. `host/webserver`'s `port` widening, the macOS directory
|
|
419
|
+
picker, the PTC call registry, and the new `job/registered` / `job/settled`
|
|
420
|
+
events are all unreachable or safely skipped by this plugin (`projectEvent`'s
|
|
421
|
+
`default: return null`). Settings surfaces are unchanged at the contract level:
|
|
422
|
+
`ui-settings`'s `settings.section` slot has the same SHA as alpha.2, and
|
|
423
|
+
`configForms.get()` kept its name. The Web composition still mounts
|
|
424
|
+
`schedule`/`ui-schedule`, and `directory-picker` is still disabled in place, so
|
|
425
|
+
the plugin's three `cordis.patch.yml` rows still land.
|
|
426
|
+
|
|
427
|
+
### Validation evidence
|
|
428
|
+
|
|
429
|
+
- **Config schema on the published CLI:** isolated `DSH_HOME`, `dsh
|
|
430
|
+
--profile deeppilot-audit --from-default-profile sdk-minimal
|
|
431
|
+
--dump-config-schema --patch cordis.patch.yml` against
|
|
432
|
+
`@deepseek-ai/dsh@0.2.1-alpha.3` → exit 0, 179,851 bytes, the plugin entry at
|
|
433
|
+
`/37` reports `status: 'schema'` with `configRef: #/$defs/config24`, all seven
|
|
434
|
+
volatile assertions pass, no `deeppilot`-attributed diagnostic, and the only
|
|
435
|
+
warning is the `sdk-minimal` template's own missing `directory-picker` row.
|
|
436
|
+
- **Web composition:** the same CLI composed a real Web profile with the plugin
|
|
437
|
+
patch → **1225 rows versus 1214 without it**, the plugin's three rows present
|
|
438
|
+
at `deeppilot-directory-picker-browse` / `…-browse-client` / `deeppilot`, and
|
|
439
|
+
zero `unrecognized Loader tree carrier` entries on either side.
|
|
440
|
+
- **Plugin unit tests:** `npx tsx --test tests/*.test.ts` → **562 tests, 561
|
|
441
|
+
pass, 0 fail, 1 todo**, including the six new contract tests. `npm run
|
|
442
|
+
typecheck`, `npm run build`, and `npm run check:config-schema` (against the
|
|
443
|
+
pinned `0.2.0-rc.2` CLI) all pass with regenerated `lib/`.
|
|
444
|
+
- **Live A/B on a real `0.2.1-alpha.3` host — the evidence that matters.** An
|
|
445
|
+
isolated `DSH_HOME` profile built from the shipped Web template and linked to
|
|
446
|
+
this working tree booted under the published alpha.3 CLI (`--port 3180`; the
|
|
447
|
+
default 3080 was already held by this machine's own Host). The **unfixed**
|
|
448
|
+
`0.9.4` build printed
|
|
449
|
+
`[deeppilot] DSH Remote interaction client unavailable:
|
|
450
|
+
@deepseek-ai/dsh-typert-registry/client Client bundle returned no exports`
|
|
451
|
+
and the fixed build printed no such line. The checklist itself scored
|
|
452
|
+
**23 pass / 1 fail / 4 skipped in both runs** — see the blind spot below.
|
|
453
|
+
- **The regression checklist does not cover this seam.** `scripts/smoke-live.mts`
|
|
454
|
+
scored identically before and after the fix, because the resident Client it
|
|
455
|
+
exercises is only reachable through the approval/question waterfall, and step
|
|
456
|
+
10 is skipped without `--interactions`. The observable difference is the Host
|
|
457
|
+
log line and the phone's approval/question delivery, not a checklist step. A
|
|
458
|
+
future revision should assert on that diagnostic directly.
|
|
459
|
+
- **Backward compatibility, same working tree:** the same profile booted under
|
|
460
|
+
the published `0.2.1-alpha.2` CLI (`--port 3181`) scored **25 pass / 0 fail /
|
|
461
|
+
0 warn / 3 skipped** with no interaction-client diagnostic — the same result
|
|
462
|
+
the alpha.2 audit recorded, so the fix does not regress the host users run
|
|
463
|
+
today.
|
|
464
|
+
- **One checklist difference between the two host versions is real.**
|
|
465
|
+
`模型目录可读` passes on alpha.2 and fails on alpha.3 in the same isolated
|
|
466
|
+
home (`0 provider / 0 model, routable=true`): this release stops listing a
|
|
467
|
+
pi-ai route before its credential resolves and returns only the version-gated
|
|
468
|
+
subset for the account route. The home has no model credential, so the phone's
|
|
469
|
+
model list is empty there. That is the documented behavior change, not a
|
|
470
|
+
plugin defect.
|
|
471
|
+
- **Artifact inspection:** the `node`/`browser` conditional exports and the
|
|
472
|
+
banner counts above were read from the installed alpha.3 packages, not from
|
|
473
|
+
the diff.
|
|
474
|
+
|
|
475
|
+
### Host-side migrations this machine must apply
|
|
476
|
+
|
|
477
|
+
Neither is a plugin defect, and neither is fixed by the plugin change:
|
|
478
|
+
|
|
479
|
+
1. **`llm-deepseek-account`'s retired `models` key.**
|
|
480
|
+
`~/.dsh/profiles/web/cordis.patch.yml:77-79` sets `models: []`.
|
|
481
|
+
`resolveAccountOptions` throws for *any* own `models` key
|
|
482
|
+
(`llm-deepseek-account: models is no longer configurable…`), and the
|
|
483
|
+
upstream test pins `[{ models: [] }, /models is no longer configurable/]`.
|
|
484
|
+
The empty array is not exempt. The `desktop` profile's row carries no
|
|
485
|
+
`config` and is unaffected. Whether the throw takes the whole Host down or
|
|
486
|
+
only retires that route was **not** measured — the guide says the account
|
|
487
|
+
models stop appearing either way.
|
|
488
|
+
2. **`--from-default-profile` now replaces.** It stages a fresh copy over the
|
|
489
|
+
target and deletes the previous directory, including `node_modules` and the
|
|
490
|
+
user's own `cordis.patch.yml`. The re-audit procedure below and the alpha.2
|
|
491
|
+
entry's schema command both use that flag; from now on it must be pointed at
|
|
492
|
+
a scratch name.
|
|
493
|
+
|
|
494
|
+
Checked and **not** required: Azure provider rename (no `azure` /
|
|
495
|
+
`azure-openai-responses` key exists under `~/.dsh/profiles`), credential-file
|
|
496
|
+
extra keys (the file's top level is already `version`/`refs`/`records`), and
|
|
497
|
+
the stored-credential precedence flip (no `refs` name is exported in the shell,
|
|
498
|
+
and the stored value won precedence over `.env` on both sides of the change).
|
|
499
|
+
|
|
500
|
+
### Still unverified
|
|
501
|
+
|
|
502
|
+
- Whether breaks 1 and 2 surface inside `dsh-api-proxy.ts`'s catch or escape it
|
|
503
|
+
as an unhandled rejection — the Gateway opens its Remote Events generation
|
|
504
|
+
asynchronously, after `startDshRemoteInteractions` has already returned.
|
|
505
|
+
- Whether an old profile that predates `@deepseek-ai/dsh-api-remote-access`
|
|
506
|
+
still gets the row (the shipped Web template does; `dsh-base` carries it).
|
|
507
|
+
- How the reshaped `contextBreakdown` renders on iOS, and what the phone shows
|
|
508
|
+
for the unmapped `CLIENT_UPGRADE_REQUIRED` / `MODEL_UNSUPPORTED` codes.
|
|
509
|
+
- Whether the plugin's own `lib/client.js` (banner shape, `dsh.client.inject`,
|
|
510
|
+
`platform: web`) is still accepted by alpha.3's client-modules scanner; that
|
|
511
|
+
scanner lives in `packages/client/modules` and `packages/client/web`, which
|
|
512
|
+
were not read in full.
|
|
513
|
+
|
|
514
|
+
### Re-audit procedure used
|
|
515
|
+
|
|
516
|
+
Fetch the new tag into the existing blobless clone at `.audit-dsh-alpha2`
|
|
517
|
+
(`git fetch --filter=blob:none --no-tags origin
|
|
518
|
+
'+refs/tags/<tag>:refs/tags/<tag>'`, ~3 s), slice
|
|
519
|
+
`git diff --name-status` per package, and write each slice's content diff to a
|
|
520
|
+
file before parallel reading — blobless clones fetch blobs on demand, so one
|
|
521
|
+
`git diff` per region is far cheaper than many small reads. Reproduce a
|
|
522
|
+
suspected carrier break against the **published** `lib/` bundles with a stub
|
|
523
|
+
`this`, as above, rather than trusting either the diff or the release notes.
|
|
524
|
+
`npm --cache <workspace dir>` is still required; `~/.npm/_cacache` holds
|
|
525
|
+
root-owned files and fails `npm view` with `EPERM`.
|
|
526
|
+
|
|
527
|
+
## 2026-10-09 — audit of `dsh-v0.2.1-alpha.2`
|
|
528
|
+
|
|
529
|
+
### Scope and method
|
|
530
|
+
|
|
531
|
+
Compared `dsh-v0.2.1-alpha.1...dsh-v0.2.1-alpha.2` (**669 commits, 3226 changed
|
|
532
|
+
files, 114,987 insertions, 28,177 deletions**) from a full clone
|
|
533
|
+
(`git clone --filter=blob:none --no-checkout`, unshallowed before measuring).
|
|
534
|
+
Baseline SHA `5badb150`, target SHA `d7432673`, annotated tag object `6851e496`.
|
|
535
|
+
Peer package names were mapped to monorepo paths by reading every `package.json`
|
|
536
|
+
in the tree, then each peer package's `src/**/*.ts` was diffed with
|
|
537
|
+
`git diff --name-status`. Every changed source file under `packages/api`,
|
|
538
|
+
`packages/session*`, `packages/client`, and `packages/core` was read in full —
|
|
539
|
+
including the complete 836-line session-controller diff — rather than being
|
|
540
|
+
summarised from release notes. All **20** upgrade guides added since the last
|
|
541
|
+
audit (19 under `docs/upgrade-guide/v0.2.1-alpha.1/` plus `bundled-python-tests`
|
|
542
|
+
under `v0.2.0-rc.2/`) were read in Chinese in full, and each one that could touch
|
|
543
|
+
an integration seam was traced to its implementation before being judged. No
|
|
544
|
+
guide from an earlier release was modified between the two tags.
|
|
545
|
+
|
|
546
|
+
### Peer packages: 2 of 12 changed source
|
|
547
|
+
|
|
548
|
+
| Package | Path | Changed `src/` files |
|
|
549
|
+
| --- | --- | --- |
|
|
550
|
+
| `@deepseek-ai/dsh` | `apps/cli` | 1 (`src/args.ts`) |
|
|
551
|
+
| `@deepseek-ai/dsh-client-connection` | `packages/client/connection` | 5 (`api-request-trust.ts`, `browser-auth.ts`, `index.ts`, `rpc-host.ts`, `rpc.ts`) |
|
|
552
|
+
| `@deepseek-ai/dsh-api-gateway` | `packages/api/gateway` | 0 |
|
|
553
|
+
| `@deepseek-ai/dsh-api-remotes` | `packages/api/remotes` | 0 |
|
|
554
|
+
| `@deepseek-ai/dsh-client-locale` | `packages/client/locale` | 0 |
|
|
555
|
+
| `@deepseek-ai/dsh-client-ui-settings` | `packages/client/ui-settings` | 0 |
|
|
556
|
+
| `@deepseek-ai/dsh-client-ui-slots` | `packages/client/ui-slots` | 0 |
|
|
557
|
+
| `@deepseek-ai/dsh-settings` | `packages/settings/settings` | 0 |
|
|
558
|
+
| `@deepseek-ai/dsh-typert-protocol` | `packages/typert/protocol` | 0 |
|
|
559
|
+
| `@deepseek-ai/dsh-typert-registry` | `packages/typert/registry` | 0 |
|
|
560
|
+
| `@deepseek-ai/cordis` | `vendor/cordis` | 0 |
|
|
561
|
+
| `@deepseek-ai/schemastery` | `vendor/schemastery` | 0 |
|
|
562
|
+
|
|
563
|
+
`react` is not an upstream package and was not measured. Two non-peer packages
|
|
564
|
+
that the bridge reaches through `ctx.get` did change and are covered below:
|
|
565
|
+
`packages/api/session-controller` and `packages/client/connection`.
|
|
566
|
+
|
|
567
|
+
### The one confirmed break
|
|
568
|
+
|
|
569
|
+
`packages/api/session-controller/src/index.ts` started dereferencing a parameter
|
|
570
|
+
that upstream's own published declaration already marks as required:
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
async projections(request: SessionProjectionsRequest, signal: AbortSignal): Promise<SessionProjectionsValue> {
|
|
574
|
+
const { sessionId } = request
|
|
575
|
+
if (sessionId.length === 0) throw new RemoteError('gateway/bad-request', ...)
|
|
576
|
+
try {
|
|
577
|
+
signal.throwIfAborted() // now the first statement
|
|
578
|
+
const live = this.liveProjections(sessionId)
|
|
579
|
+
...
|
|
580
|
+
} catch (error) {
|
|
581
|
+
...
|
|
582
|
+
if (signal.aborted || ...) throw new RemoteError('gateway/cancelled', ...) // now unconditional
|
|
583
|
+
...
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
In alpha.1 the same method only forwarded `signal` into `observeSession` and read
|
|
589
|
+
`signal.aborted` inside the `catch`, so an omitted signal still survived the happy
|
|
590
|
+
path. In alpha.2 the omission throws before any work is done — reproduced against
|
|
591
|
+
the published `0.2.1-alpha.2` bundle with the real class, a stub context whose
|
|
592
|
+
live-projection path returns a value, and no signal:
|
|
593
|
+
|
|
594
|
+
```sh
|
|
595
|
+
# published @deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2, lib/index.js
|
|
596
|
+
SessionController.prototype.projections.call(receiver, { sessionId: 'x' })
|
|
597
|
+
# → TypeError (signal is undefined at signal.throwIfAborted(), then again at signal.aborted)
|
|
598
|
+
SessionController.prototype.projections.call(receiver, { sessionId: 'x' }, new AbortController().signal)
|
|
599
|
+
# → { kind: 'sequenced', asOfSeq: 7, values: { title: 'audit' } }
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
DeepPilot's call site is `src/dsh-api-proxy.ts:235`:
|
|
603
|
+
|
|
604
|
+
```ts
|
|
605
|
+
projections: async (request) => this.call(() => this.session.projections(request.payload!)),
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
The hand-written mirror at `src/dsh-api-proxy.ts:45` declares `signal?` optional,
|
|
609
|
+
which is exactly what let the omission typecheck; the upstream declaration
|
|
610
|
+
(`@deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2`,
|
|
611
|
+
`lib/types/index.d.ts:209`) requires it. `DshApiProxy.call()`
|
|
612
|
+
(`src/dsh-api-proxy.ts:413`) converts the `TypeError` into
|
|
613
|
+
`{ ok: false, error: { code: 'internal' } }`, so this degrades rather than
|
|
614
|
+
crashes: `HostBridge.refreshProjections` (`src/host-bridge.ts:799`) already treats
|
|
615
|
+
a failed baseline as a diagnostic and logs
|
|
616
|
+
`sessions.projections failed: internal`.
|
|
617
|
+
|
|
618
|
+
**Observable effect.** The phone's session summary loses the on-open complete
|
|
619
|
+
projection baseline. It still receives title, todo counts, and token usage from
|
|
620
|
+
the session list rows' cached projections (`src/dsh-api-proxy.ts:458`,
|
|
621
|
+
`src/host-bridge.ts:1546`) and from live `session/projection` frames
|
|
622
|
+
(`src/host-bridge.ts:453`), so nothing breaks outright; the loss shows up on
|
|
623
|
+
hosts where the baseline read was the only source — a session whose list row
|
|
624
|
+
carries no cached projections stays without those values longer than it should.
|
|
625
|
+
Cold history reads are **not** affected: they run through
|
|
626
|
+
`inspect(sessionId)`, whose signal is still optional upstream.
|
|
627
|
+
|
|
628
|
+
**Fix (applied 2026-10-10).** The adapter now passes a signal the way `list` and
|
|
629
|
+
`prompt` already do —
|
|
630
|
+
`this.session.projections(request.payload!, new AbortController().signal)` — and
|
|
631
|
+
the mirror's `signal` is required, so the compiler keeps it that way.
|
|
632
|
+
`refreshProjections` is fire-and-forget and has no cancellation source of its
|
|
633
|
+
own, so a dedicated long-lived controller is not warranted; following the
|
|
634
|
+
existing convention in the same file is enough. `lib/index.js` was regenerated.
|
|
635
|
+
|
|
636
|
+
The fix ships in `0.9.4`, published 2026-10-10 (see "Plugin release verdicts").
|
|
637
|
+
A host on `0.2.1-alpha.2` left on the published `0.9.3` still loses the on-open
|
|
638
|
+
projection baseline. That was observed directly: on a live `0.2.1-alpha.2` host
|
|
639
|
+
running published `0.9.3` the diagnostic appeared, and on the same host version
|
|
640
|
+
running this build it did not (see "Live run on a real `0.2.1-alpha.2` host"
|
|
641
|
+
under Validation evidence).
|
|
642
|
+
|
|
643
|
+
`tests/dsh-api-proxy.test.ts` adds "projection baseline reads pass the
|
|
644
|
+
AbortSignal the Host now requires": a controller stub that calls
|
|
645
|
+
`signal.throwIfAborted()` before returning, plus an assertion that the adapter
|
|
646
|
+
handed it a real `AbortSignal`. The test was verified red against the previous
|
|
647
|
+
call form (1 failure) and green after the fix, so it pins the behaviour rather
|
|
648
|
+
than the implementation coincidence.
|
|
649
|
+
|
|
650
|
+
### Other session-controller changes, checked against this plugin
|
|
651
|
+
|
|
652
|
+
`Where` names the upstream `packages/api/session-controller/src/` file unless it
|
|
653
|
+
says otherwise; `lib/types` means the published declarations of
|
|
654
|
+
`@deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2`.
|
|
655
|
+
|
|
656
|
+
| Change | Where | DeepPilot impact |
|
|
657
|
+
| --- | --- | --- |
|
|
658
|
+
| `projections` return type is now a union: `{ kind: 'sequenced', asOfSeq, values }` / `{ kind: 'migration-required', values }` / `null` | `src/index.ts:506-541` | The mirror's flat `{ asOfSeq, values }` type is now narrower than upstream, but the plugin reads only `values`, so a successful read still folds. Type-mirror drift, not a runtime defect. |
|
|
659
|
+
| `inspect(sessionId, signal?)` | `packages/session/session-query` | Signal is still optional in the published declaration, so the plugin's `inspect(sessionId)` call keeps working. Cold log reading, scanner row decoding, and the V4 codec path were read and are unchanged for this caller. |
|
|
660
|
+
| `search(request, signal)` requires a signal | `lib/types/index.d.ts:93` | The plugin never calls `search`. |
|
|
661
|
+
| `page(request, signal)` requires a signal | `src/index.ts:1412` | Not called; history paging still goes through `inspect`. |
|
|
662
|
+
| `fork` gains `allowMigration` (default `true`) | `src/index.ts` | The plugin does not send it, so the upstream default keeps automatic migration. The new `session/migration-required` error is unreachable from the phone. |
|
|
663
|
+
| `skill-catalog` injects `workingDirectory` and lists skills by execution directory | `src/skill-catalog.ts` | Not consumed; the plugin lists no skills. |
|
|
664
|
+
| Session ownership helper | `packages/api/session-controller` | Unchanged; `session/agent-busy` still maps to `E_BUSY`. |
|
|
665
|
+
| Subagent catalog v2 `mode: 'external'` | `packages/subagent/subagent` | Descriptor version is still 3 and the earlier one-shot read types are retained; the plugin filters by origin/parent and does not fold catalogs. |
|
|
666
|
+
|
|
667
|
+
### Connection carrier: unchanged on the paths this plugin uses
|
|
668
|
+
|
|
669
|
+
`packages/client/connection` gained five changed source files, including a new
|
|
670
|
+
`api-request-trust.ts` and browser-auth changes. Those trust, authentication, and
|
|
671
|
+
TLS checks run through `requestRejection`/`admit`, which guard **physical**
|
|
672
|
+
requests. The plugin's resident client uses
|
|
673
|
+
`connection.createSharedFetchHandler('/api')`, which dispatches routes directly
|
|
674
|
+
and never calls `admit` (`packages/client/connection/src/rpc-host.ts:136-154`), and
|
|
675
|
+
its stream call keeps the
|
|
676
|
+
five-argument `wireStream.open(endpoint, payload, uplink, peer, signal)` order.
|
|
677
|
+
The plugin's own LAN TLS pin check and `/phone` listener are unaffected.
|
|
678
|
+
|
|
679
|
+
### Four-way comparison at `0.2.1-alpha.2`
|
|
680
|
+
|
|
681
|
+
| Surface | Both sides | Upstream has, plugin does not consume | Plugin has, upstream does not |
|
|
682
|
+
| --- | --- | --- | --- |
|
|
683
|
+
| Session RPCs | `list`, `inspect`, `create`, `fork`, `rename`, `cancel`, `prompt`, `attachment`, `selectModel`, `modelCatalog`, `projections` | `search`, `page`, `follow`, `updateQueue`, `initializeDefaultModel`, `canOpenWorkspacePath`, `openWorkspacePath`, `workspacePathApplications` | phone-shaped `sessions.*` envelope, history windowing, projection folding, notification suppression |
|
|
684
|
+
| Workspace RPCs | `create`, `archiveSession`, `unarchiveSession`, `follow` | `initializeDefault`, `rename`, `delete`, `insertBefore`, `insertSessionBefore`, `pinSession`, `unpinSession` | archived mirror, `sessionRestore` capability bit |
|
|
685
|
+
| Directory picker | `list`, `pick` | `createDirectory` | browse host/client patch rows, phone-side directory browsing |
|
|
686
|
+
| Schedule | `list`, `history`, `create`, `update`, `delete` | `catalog`, stream delivery modes | phone schedule frames, `schedule.manage` scope, prompt-free mutation journal |
|
|
687
|
+
| Projections | reading `values` | `workingDirectory`, `formatStatus`, the migration-required branch | `title` / `todos` / `sessionStats` / `tokenUsage` folding |
|
|
688
|
+
| Subagents | — | `prompt`, `interruptByParent`, catalog v2 | subagent-session exclusion and notification policy |
|
|
689
|
+
| Trust and auth | shared Fetch route dispatch, TLS pin check | `webStartup.trustedHosts`, listener-address validation | device records, pairing tokens, signed challenge |
|
|
690
|
+
|
|
691
|
+
Rows marked "upstream has, plugin does not consume" are capability gaps by
|
|
692
|
+
choice, not regressions: none of them changes a call the bridge already makes.
|
|
693
|
+
`neither side` is empty for every row that matters to the phone.
|
|
694
|
+
|
|
695
|
+
### Validation evidence
|
|
696
|
+
|
|
697
|
+
- **Unit tests:** `tsx --test tests/*.test.ts` → **537 pass, 0 fail, 0 skipped**
|
|
698
|
+
on the source diff alone. After the adapter fix and its new regression test the
|
|
699
|
+
same command reports **538 pass, 0 fail, 0 skipped**, and `npm run typecheck`
|
|
700
|
+
and `npm run build` pass with regenerated `lib/`.
|
|
701
|
+
- **Config schema on the published CLI:** an isolated `DSH_HOME` and npm cache ran
|
|
702
|
+
`dsh --profile deeppilot-audit --from-default-profile sdk-minimal
|
|
703
|
+
--dump-config-schema --patch cordis.patch.yml` against
|
|
704
|
+
`@deepseek-ai/dsh@0.2.1-alpha.2` → the plugin entry reports
|
|
705
|
+
`status: 'schema'` with its own `$defs` config, the volatile-field checks pass,
|
|
706
|
+
and the only warning is the `sdk-minimal` template's own missing
|
|
707
|
+
`directory-picker` row.
|
|
708
|
+
- **Web composition:** the same CLI composed a real Web profile with the plugin
|
|
709
|
+
patch → 188 rows versus 185 without it; the plugin's three rows
|
|
710
|
+
(`deeppilot-directory-picker-browse`, `…-browse-client`, `deeppilot`) are
|
|
711
|
+
present with the plugin config schema at `#/$defs/config100`. The four
|
|
712
|
+
`unrecognized Loader tree carrier` errors the dump prints at `/181`–`/184`
|
|
713
|
+
reproduce **identically without** the patch, so they belong to the base Web
|
|
714
|
+
profile and not to this plugin.
|
|
715
|
+
- **Published client bundles:** the two resident-client tests
|
|
716
|
+
(`tests/dsh-wire-stream-contract.test.ts`) were re-run against the actual
|
|
717
|
+
published `0.2.1-alpha.2` client bundles, resolved through a temporary
|
|
718
|
+
resolution hook instead of the rc.2 dev pins → pass. This covers client-face
|
|
719
|
+
composition and the stream's abort-signal argument contract with a **mocked**
|
|
720
|
+
Host gateway.
|
|
721
|
+
- **Documentation corrected in the same change:** `COMPATIBILITY.md`,
|
|
722
|
+
`PROTOCOL.md`, `docs/DEEPPILOT_FEATURE_PLAN.md`, and the schedule-service
|
|
723
|
+
comment in `src/dsh-api-proxy.ts` no longer tell users to enable a retired
|
|
724
|
+
bundle. The private app's schedule-unsupported strings still do; that copy
|
|
725
|
+
lives in the other repository and is listed as an open follow-up above.
|
|
726
|
+
- **Live run on a real `0.2.1-alpha.2` host (2026-10-10).** An isolated
|
|
727
|
+
`DSH_HOME` (`.audit-smoke-home`, workspace root) was given a `smoke` profile
|
|
728
|
+
built from the shipped Web template with this working tree linked in, plus a
|
|
729
|
+
second profile pinning the published `dsh-deeppilot@0.9.3`. The published
|
|
730
|
+
`0.2.1-alpha.2` CLI booted each one in turn and
|
|
731
|
+
`scripts/smoke-live.mts run` drove the phone protocol against it over the LAN
|
|
732
|
+
TLS listener (no `--prompt`, so no model turn). **Both runs: 25 pass / 0 fail /
|
|
733
|
+
0 warn / 3 skipped** — health, TLS SPKI, pairing rejection, WSS upgrade,
|
|
734
|
+
challenge/prove with all six scopes, session list/create/open/history,
|
|
735
|
+
model catalog and switch, workspace list/create, archive/unarchive, pending
|
|
736
|
+
snapshot, the full schedule create/history/delete flow, and reconnect replay.
|
|
737
|
+
The skips are the log-pin cross-check (needs `SMOKE_LOG`), the real prompt, and
|
|
738
|
+
the approval/question round trip.
|
|
739
|
+
- **The A/B result is the evidence that matters.** The published `0.9.3` host
|
|
740
|
+
logged `[deeppilot] sessions.projections failed: internal` while still passing
|
|
741
|
+
every checklist step, which is exactly the silent degradation predicted above;
|
|
742
|
+
the working-tree build, same checklist, same host version, logged **zero**
|
|
743
|
+
`sessions.projections` lines. The defect and the fix are therefore both
|
|
744
|
+
confirmed at runtime on `0.2.1-alpha.2`, not only in unit tests.
|
|
745
|
+
- **The live run also settles the automation question.** The welcome frame on a
|
|
746
|
+
stock Web profile advertised `schedules: true` with no bundle enabled and no
|
|
747
|
+
user action, and the full schedule create/history/delete flow passed against
|
|
748
|
+
it. That is the behaviour the corrected `COMPATIBILITY.md`, `PROTOCOL.md`, and
|
|
749
|
+
feature-plan copy now describe.
|
|
750
|
+
- **Still untested:** a real model turn inside the plugin's live path (the
|
|
751
|
+
isolated home has no model credentials), the Funnel/remote transport (the
|
|
752
|
+
isolated host ran `remote.enabled` at its default `false`; the live host's
|
|
753
|
+
phone path goes through the Funnel helper), and a real iPhone. The live smoke
|
|
754
|
+
gate still refuses to run against the machine's own host without a restart,
|
|
755
|
+
because `DeviceStore` snapshots `devices-v2.json` once at mount.
|
|
756
|
+
- **Repository pins unchanged:** `npm run check:config-schema` and the CI
|
|
757
|
+
`config-schema` job still target the `0.2.0-rc.2` CLI. The alpha.2 schema dump
|
|
758
|
+
above was run separately in an isolated home instead of re-pinning the
|
|
759
|
+
repository's own gate, so the gate keeps reporting its `0.2.0-rc.2` baseline
|
|
760
|
+
and nothing in the working tree depends on alpha.2.
|
|
761
|
+
|
|
762
|
+
### Re-audit procedure used
|
|
763
|
+
|
|
764
|
+
The same full-clone method as the alpha.1 entry, with one addition: because two
|
|
765
|
+
non-peer packages (`api/session-controller`, `client/connection`) carry the
|
|
766
|
+
integration seams this plugin depends on, the diff was extended past the peer
|
|
767
|
+
set and every changed source file in `packages/api`, `packages/client`,
|
|
768
|
+
`packages/session*`, and `packages/core` was read in full. Judging only the peer
|
|
769
|
+
list would have missed the `projections` break entirely — the peer-list check
|
|
770
|
+
reported 10 of 12 packages clean while the defect sat in a package reached
|
|
771
|
+
through `ctx.get('sessionController')`.
|
|
772
|
+
|
|
103
773
|
## 2026-10-03 — audit of `dsh-v0.2.1-alpha.1`
|
|
104
774
|
|
|
105
775
|
### Scope and method
|