dsh-zen-remote 2.2.1 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +23 -5
  2. package/README.zh-CN.md +23 -5
  3. package/docs/interface.md +1 -1
  4. package/docs/plugin-bridge.md +1 -1
  5. package/docs/remote-access.en.md +13 -1
  6. package/docs/remote-access.md +14 -2
  7. package/lib/adapters/dsh-better-sidebar/index.js +25 -13
  8. package/lib/adapters/dsh-better-sidebar/index.js.map +1 -1
  9. package/lib/adapters/dsh-better-sidebar/methods.js +30 -7
  10. package/lib/adapters/dsh-better-sidebar/methods.js.map +1 -1
  11. package/lib/admin-routes.js +3 -2
  12. package/lib/admin-routes.js.map +1 -1
  13. package/lib/index.js +16 -1
  14. package/lib/index.js.map +1 -1
  15. package/lib/intercept-shape.js +10 -4
  16. package/lib/intercept-shape.js.map +1 -1
  17. package/lib/intercept.js +47 -12
  18. package/lib/intercept.js.map +1 -1
  19. package/lib/invoke-binary.js +126 -0
  20. package/lib/invoke-binary.js.map +1 -0
  21. package/lib/owner-peer.js +72 -0
  22. package/lib/owner-peer.js.map +1 -0
  23. package/lib/relay-server.js +18 -10
  24. package/lib/relay-server.js.map +1 -1
  25. package/lib/types/adapters/dsh-better-sidebar/index.d.ts +16 -11
  26. package/lib/types/adapters/dsh-better-sidebar/index.d.ts.map +1 -1
  27. package/lib/types/adapters/dsh-better-sidebar/methods.d.ts +16 -7
  28. package/lib/types/adapters/dsh-better-sidebar/methods.d.ts.map +1 -1
  29. package/lib/types/admin-routes.d.ts +2 -0
  30. package/lib/types/admin-routes.d.ts.map +1 -1
  31. package/lib/types/index.d.ts.map +1 -1
  32. package/lib/types/intercept-shape.d.ts.map +1 -1
  33. package/lib/types/intercept.d.ts +11 -0
  34. package/lib/types/intercept.d.ts.map +1 -1
  35. package/lib/types/invoke-binary.d.ts +41 -0
  36. package/lib/types/invoke-binary.d.ts.map +1 -0
  37. package/lib/types/owner-peer.d.ts +57 -0
  38. package/lib/types/owner-peer.d.ts.map +1 -0
  39. package/lib/types/relay-server.d.ts +8 -0
  40. package/lib/types/relay-server.d.ts.map +1 -1
  41. package/llms.txt +1 -1
  42. package/package.json +36 -36
  43. package/src/adapters/dsh-better-sidebar/index.ts +25 -13
  44. package/src/adapters/dsh-better-sidebar/methods.ts +30 -7
  45. package/src/admin-routes.ts +5 -2
  46. package/src/index.ts +16 -1
  47. package/src/intercept-shape.ts +10 -3
  48. package/src/intercept.ts +55 -9
  49. package/src/invoke-binary.ts +134 -0
  50. package/src/owner-peer.ts +81 -0
  51. package/src/relay-server.ts +27 -11
@@ -80,6 +80,7 @@
80
80
  */
81
81
  import type { RelayClient, RelayClock } from './relay-client.js';
82
82
  import type { GatewayShapeCheck } from './intercept-shape.js';
83
+ import { type ResultAttachment } from './invoke-binary.js';
83
84
  /** The session-locating argument fields, as registered per method. Identical
84
85
  * in name and meaning to relay-access.ts's `SessionField`; a field named
85
86
  * without a dot is TOP-LEVEL. `request.workspaceId` locates a remote create
@@ -111,6 +112,7 @@ export declare const CLIENT_METHOD_FIELDS: Readonly<Record<string, readonly Sess
111
112
  export type DispatchEnvelope = {
112
113
  ok: true;
113
114
  value: unknown;
115
+ attachments?: ResultAttachment[];
114
116
  } | {
115
117
  ok: false;
116
118
  error: {
@@ -400,6 +402,11 @@ export declare function installIntercept(options: InstallInterceptOptions): Inte
400
402
  export interface SelfCheckOptions {
401
403
  /** How long the probe may wait for its first frame; default 5000 ms. */
402
404
  timeoutMs?: number;
405
+ /** The owner Peer the probe opens as. DSH 0.2.1-alpha.3 removed
406
+ * `gateway.operatorPeer()` and requires a live Peer on every stream; see
407
+ * owner-peer.ts. Undefined falls back to `operatorPeer()` where it still
408
+ * exists (≤ alpha.2), else to no Peer. */
409
+ getPeer?: () => unknown;
403
410
  }
404
411
  /**
405
412
  * The startup behavior self-check (spike §4.1 check 4): open the one stream
@@ -424,6 +431,10 @@ export interface RunSelfCheckOptions {
424
431
  retryDelayMs?: number;
425
432
  /** Failure logging, wired to the context logger by index.ts. */
426
433
  log?: (format: string, ...args: unknown[]) => void;
434
+ /** Success logging (one line once the probe passes). */
435
+ info?: (format: string, ...args: unknown[]) => void;
436
+ /** Forwarded to {@link behaviorSelfCheck}. */
437
+ getPeer?: () => unknown;
427
438
  }
428
439
  /**
429
440
  * Run the behavior self-check with the wiring's failure policy: ONE retry
@@ -1 +1 @@
1
- {"version":3,"file":"intercept.d.ts","sourceRoot":"","sources":["../../src/intercept.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"intercept.d.ts","sourceRoot":"","sources":["../../src/intercept.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * invoke 二进制载荷的线上标记(2026-10-14 修复):`v1/invoke` 是纯 JSON
3
+ * 通道,而 `workspaceFiles/readBytes` 的结果带 `data: Uint8Array`——直接
4
+ * `JSON.stringify` 会把 typed array 打成普通对象,子客户端侧的 zod 校验
5
+ * (expected "Uint8Array")当场拒绝,远程会话的图片预览报「读取失败」。
6
+ *
7
+ * 两端共用同一份规则:服务端在应答前对已知二进制方法的结果做深度标记
8
+ * (typed array → `{ $zr-u8: <base64> }`),子客户端在 forwardInvoke 拿回
9
+ * 值后还原成真 `Uint8Array`。两端都按同一个 `INVOKE_BINARY_RESULT_METHODS`
10
+ * 常量开关——单一来源,不可能漂移(这是登记表同步硬约束之外的第三张
11
+ * 「小表」,但它只此一处定义)。
12
+ *
13
+ * 纯逻辑:不 import DSH、不碰 I/O。
14
+ */
15
+ /** 标记键。线上只会出现服务端写入的标记;客户端只认这个键。 */
16
+ export declare const BINARY_MARKER = "$zr-u8";
17
+ /** 结果里带二进制的方法(`namespace/method`)。请求侧参数不带二进制
18
+ * (readBytes 的参数是 scope/paths),故只有结果方向需要标记。 */
19
+ export declare const INVOKE_BINARY_RESULT_METHODS: ReadonlySet<string>;
20
+ /** 深度遍历,把 typed array(含 Buffer)换成 `{ $zr-u8: base64 }` 标记。 */
21
+ export declare function markInvokeBinary(value: unknown, depth?: number): unknown;
22
+ /** 深度遍历,把 `{ $zr-u8: base64 }` 标记还原成 `Uint8Array`。
23
+ * 标记只会是本模块的 `markInvokeBinary` 写下的(键名即凭据),base64
24
+ * 恒有效;万一混入同键的怪值,`Buffer.from(..., 'base64')` 的宽容解码
25
+ * 也不会抛。非标记对象原样返回(同一引用,不惊动浅比较的调用方)。 */
26
+ export declare function restoreInvokeBinary(value: unknown, depth?: number): unknown;
27
+ /** dispatchRpc 信封的附件旁路形状(dsh-api-gateway 的 encodeRpcResult):
28
+ * Uint8Array 不进 JSON 值,抽到 `attachments: [{path, bytes}]`,原位置
29
+ * 置 null,渲染层按 path 回填。远程路径必须产出同样的契约,否则
30
+ * typed array 在后台→浏览器的桥上被 JSON 压扁(2026-10-14 实机定位)。 */
31
+ export interface ResultAttachment {
32
+ path: (string | number)[];
33
+ bytes: Uint8Array;
34
+ }
35
+ /** 把(已还原的)结果值里的 Uint8Array 抽成附件;值内原位置置 null。
36
+ * 没有 typed array 时 value 原引用返回、attachments 为空数组。 */
37
+ export declare function extractResultAttachments(value: unknown): {
38
+ value: unknown;
39
+ attachments: ResultAttachment[];
40
+ };
41
+ //# sourceMappingURL=invoke-binary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoke-binary.d.ts","sourceRoot":"","sources":["../../src/invoke-binary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,mCAAmC;AACnC,eAAO,MAAM,aAAa,WAAW,CAAA;AAErC;+CAC+C;AAC/C,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,MAAM,CAAyC,CAAA;AAWtG,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAwBnE;AAED;;;uCAGuC;AACvC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CA4BtE;AAED;;;wDAGwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;IACzB,KAAK,EAAE,UAAU,CAAA;CAClB;AAED;qDACqD;AACrD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,gBAAgB,EAAE,CAAA;CAAE,CA8B5G"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The owner Peer this plugin speaks as when it calls the typert gateway on
3
+ * its own behalf (relay routes, admin projections, the intercept
4
+ * self-check, the `$events` relay subscription).
5
+ *
6
+ * DSH 0.2.1-alpha.3 removed the gateway's implicit operator fallback
7
+ * (`gateway.operatorPeer()`, `connection.operator` and the `OperatorPeer`
8
+ * export are gone): every gateway call — `invoke`, `stream`,
9
+ * `openWireStream`, `dispatchRpc` — now asserts a live Peer, and `$events` /
10
+ * `$events/result` additionally demand `principal.kind === 'owner'` and
11
+ * `interactions === 'answer'`, with the result answered by the SAME Peer that
12
+ * opened the stream. The sanctioned in-process door is
13
+ * `connection.createPeer({ principal: { kind: 'owner' }, interactions:
14
+ * 'answer' })` (docs/upgrade-guide/v0.2.1-alpha.2/operator-connect), whose
15
+ * Peer is disposed with the Context that read `connection`.
16
+ *
17
+ * Resolution order: a cached `createPeer` Peer (recreated once its scope is
18
+ * disposed), else `connection.browserPeer` (also an owner/answer Peer),
19
+ * else `undefined` — the pre-alpha.3 behavior, where the gateway itself
20
+ * defaulted a missing Peer to its operator.
21
+ */
22
+ /** Minimal structural view of a typert `PeerScope`. */
23
+ export interface PeerLike {
24
+ readonly signal?: AbortSignal;
25
+ }
26
+ /** The connection-service members this module reads. */
27
+ export interface PeerConnectionLike {
28
+ createPeer?: (options: {
29
+ principal: {
30
+ kind: 'owner';
31
+ };
32
+ interactions?: 'answer' | 'none';
33
+ }) => unknown;
34
+ browserPeer?: unknown;
35
+ }
36
+ /**
37
+ * Build a lazy, caching owner-Peer source over a connection-service reader.
38
+ * The reader runs per call (the service may be absent or restarted). The
39
+ * cache is keyed on the Peer's LIVENESS, not on the connection object's
40
+ * identity: `ctx.reflect.get('connection')` hands out a fresh proxy per read,
41
+ * so an identity key would mint a new Peer per call — and the gateway
42
+ * refuses a `$events/result` from any Peer but the one that opened
43
+ * `$events` (found by the alpha.3 live smoke). A restarted connection
44
+ * service disposes its Peers, which is exactly when a new one is minted.
45
+ * @param getConnection - live reader of `ctx.connection` (may return undefined).
46
+ * @returns a function answering the Peer to pass, or undefined to let the
47
+ * host default it (pre-alpha.3 hosts).
48
+ */
49
+ export declare function createOwnerPeerSource(getConnection: () => unknown): () => unknown;
50
+ /**
51
+ * Attach `peer` to one gateway request object when a Peer is available;
52
+ * otherwise return the request unchanged (pre-alpha.3 shape).
53
+ */
54
+ export declare function withPeer<T extends object>(request: T, getPeer: (() => unknown) | undefined): T & {
55
+ peer?: unknown;
56
+ };
57
+ //# sourceMappingURL=owner-peer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-peer.d.ts","sourceRoot":"","sources":["../../src/owner-peer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,uDAAuD;AACvD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,SAAS,EAAE;YAAE,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC;QAAC,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACrG,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,OAAO,GAAG,MAAM,OAAO,CAmBjF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAGnH"}
@@ -90,12 +90,14 @@ export interface RelayGateway {
90
90
  method: string;
91
91
  args: unknown;
92
92
  signal?: AbortSignal;
93
+ peer?: unknown;
93
94
  }): Promise<unknown>;
94
95
  stream(call: {
95
96
  namespace: string;
96
97
  method: string;
97
98
  args: unknown;
98
99
  signal?: AbortSignal;
100
+ peer?: unknown;
99
101
  }): Promise<AsyncIterable<unknown>>;
100
102
  /** The host's wire adapter, shared by the WebSocket mux and the local
101
103
  * transports. `open` is the async 0.2.0 method. */
@@ -143,6 +145,12 @@ export interface RelayHandlerOptions {
143
145
  * synthetic Request to the same exact-fetch route table the browser's
144
146
  * `/api` transport uses — no loopback HTTP, no login state. */
145
147
  getApiFetch?: () => ((request: Request) => Promise<Response>) | undefined;
148
+ /** The owner Peer every gateway call is made as (DSH 0.2.1-alpha.3+
149
+ * requires one; `$events` and `$events/result` need an owner Peer with
150
+ * `interactions: 'answer'`, the SAME one for both). Built by
151
+ * {@link createOwnerPeerSource}; absent or answering undefined keeps the
152
+ * pre-alpha.3 behavior (the host defaults the Peer). */
153
+ getPeer?: () => unknown;
146
154
  /** Stream heartbeat interval in ms (a `{"type":"ping"}` line that keeps
147
155
  * reverse proxies from timing the idle stream away); defaults to 15000.
148
156
  * Tests inject a small value. */
@@ -1 +1 @@
1
- {"version":3,"file":"relay-server.d.ts","sourceRoot":"","sources":["../../src/relay-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relay-server.d.ts","sourceRoot":"","sources":["../../src/relay-server.ts"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -16,7 +16,7 @@
16
16
  - Trust model: a paired desktop client is a TRUSTED DEVICE — server-user terminal without sandbox/approval gates, file previews not contained to the session dir, session creation in any workspace. "Shared only" constrains session data (lists, history, events are refused/filtered server-side; @ candidates list only shared sessions; references in messages, queue edits and slash commands naming an unshared session refuse the call), not the machine.
17
17
  - Env: `LAN_GATE_PORT` (3088), `LAN_GATE_HOST`, `LAN_GATE_TARGET_PORT` (auto-discovered), `LAN_GATE_RATE_LIMIT` (120), `LAN_GATE_TRUSTED_PROXIES`, `LAN_GATE_VAPID_SUBJECT`, `LAN_GATE_LANG`, `LAN_GATE_RELAY_SECRET` (auto), `DSH_PUSH_*`, `DSH_PUSH_APPROVAL_GRACE_MS`. Same keys (lowerCamelCase) in `lan-gate.config.json`.
18
18
  - Security: 8-char one-time role-bound pairing code (10 min TTL), 5-wrong-code lockout, revocable per-device tokens (kills connections + push subscription), 120 req/min per client IP for unpaired requests.
19
- - Known limits: remote sessions disable export (menu item hidden, backend 403 for virtual ids); changes summary/diff work remotely but the "open on the server machine" entries (open / reveal in Finder / open in app) are hidden there; with dsh-better-sidebar installed, its file surfaces (file browser, editor, git, tasks, bottom workbench, floating windows) are hidden in remote sessions because they act on the CLIENT machine's own files — so remote sessions have no sidebar file browser (it takes over the host's "Files" tab) and file links in chat or deliverables open an empty right column; @ candidates are truncated to 50 by the host BEFORE share filtering (a shared session can be missing from the list on a session-heavy server); third-party plugins' non-standard endpoints are not forwarded — their tools run on the server and are unaffected, what breaks is UI calling the plugin's own backend with a session id (better-sidebar, handled as above); both ends need zen-remote 2.0.x; server switch-back or workspace-id reuse needs a page reload; several DSH windows at once (including the desktop backend opened in a browser) can misjudge which session is currently open — closed-remote hiding can fire at the wrong moment and worst case dismiss an open session to the home page. Dev only (see AGENTS.md hard constraints): same-machine testing must reach the gateway over a LAN IP (loopback = local-direct, skips token checks) — normal two-machine use is unaffected.
19
+ - Known limits: remote sessions disable export (menu item hidden, backend 403 for virtual ids); changes summary/diff work remotely but the "open on the server machine" entries (open / reveal in Finder / open in app) are hidden there; with dsh-better-sidebar installed, UNBRIDGED versions have their file surfaces (file browser, editor, git, tasks, bottom workbench, floating windows) hidden in remote sessions because they act on the CLIENT machine's own files — 0.25.0 and 0.26.0 are bridged by the built-in adapter and keep working against the server, so only an UNBRIDGED remote session has no sidebar file browser (it takes over the host's "Files" tab) and unbridged file links in chat or deliverables open an empty right column; @ candidates are truncated to 50 by the host BEFORE share filtering (a shared session can be missing from the list on a session-heavy server); third-party plugins' non-standard endpoints are not forwarded — their tools run on the server and are unaffected, what breaks is UI calling the plugin's own backend with a session id (better-sidebar, handled as above); both ends need the same zen-remote version; server switch-back or workspace-id reuse needs a page reload; several DSH windows at once (including the desktop backend opened in a browser) can misjudge which session is currently open — closed-remote hiding can fire at the wrong moment and worst case dismiss an open session to the home page. Dev only (see AGENTS.md hard constraints): same-machine testing must reach the gateway over a LAN IP (loopback = local-direct, skips token checks) — normal two-machine use is unaffected.
20
20
 
21
21
  ## Docs
22
22
  - README.md (English, install + config + migration + security model), README.zh-CN.md (same in Chinese), AGENTS.md (repo guide), LICENSE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-zen-remote",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "description": "一个插件、两种角色:主服务端把 DeepSeek Harness 变成手机能安全访问的 PWA(移动端界面、配对码网关、Web Push),并把选定会话共享给已配对的桌面端;子客户端在另一台 DSH 桌面端的侧边栏里远程使用这些会话。One DSH plugin, two roles: the host turns DeepSeek Harness into a phone-reachable PWA (mobile UI, pairing-code gateway behind your own reverse proxy, Web Push) and shares chosen sessions to paired desktops; the client uses those sessions remotely from another DSH desktop app's sidebar.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -115,42 +115,42 @@
115
115
  }
116
116
  },
117
117
  "devDependencies": {
118
- "@deepseek-ai/cordis": "4.0.5-alpha.1",
118
+ "@deepseek-ai/cordis": "4.0.5-alpha.2",
119
119
  "@deepseek-ai/cosmokit": "1.8.5",
120
- "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
121
- "@deepseek-ai/dsh-api-remotes": "0.2.1-alpha.2",
122
- "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.2",
123
- "@deepseek-ai/dsh-api-workspace-controller": "0.2.1-alpha.2",
124
- "@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
125
- "@deepseek-ai/dsh-client-connection": "0.2.1-alpha.2",
126
- "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.2",
127
- "@deepseek-ai/dsh-client-store": "0.2.1-alpha.2",
128
- "@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.2",
129
- "@deepseek-ai/dsh-client-ui-layout": "0.2.1-alpha.2",
130
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.2",
131
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.2",
132
- "@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.2",
133
- "@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-alpha.2",
134
- "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.2",
135
- "@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.2",
136
- "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
137
- "@deepseek-ai/dsh-ptc-runtime": "0.2.1-alpha.2",
138
- "@deepseek-ai/dsh-sandbox": "0.2.1-alpha.2",
139
- "@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.2",
140
- "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
141
- "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
142
- "@deepseek-ai/dsh-session-log-export": "0.2.1-alpha.2",
143
- "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
144
- "@deepseek-ai/dsh-session-query": "0.2.1-alpha.2",
145
- "@deepseek-ai/dsh-session-stats": "0.2.1-alpha.2",
146
- "@deepseek-ai/dsh-subagent": "0.2.1-alpha.2",
147
- "@deepseek-ai/dsh-timeout": "0.2.1-alpha.2",
148
- "@deepseek-ai/dsh-token-meter": "0.2.1-alpha.2",
149
- "@deepseek-ai/dsh-tools": "0.2.1-alpha.2",
150
- "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
151
- "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.2",
152
- "@deepseek-ai/dsh-util-values": "0.2.1-alpha.2",
153
- "@deepseek-ai/dsh-workspace": "0.2.1-alpha.2",
120
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.3",
121
+ "@deepseek-ai/dsh-api-remotes": "0.2.1-alpha.3",
122
+ "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.3",
123
+ "@deepseek-ai/dsh-api-workspace-controller": "0.2.1-alpha.3",
124
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.3",
125
+ "@deepseek-ai/dsh-client-connection": "0.2.1-alpha.3",
126
+ "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.3",
127
+ "@deepseek-ai/dsh-client-store": "0.2.1-alpha.3",
128
+ "@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.3",
129
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.1-alpha.3",
130
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.3",
131
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.3",
132
+ "@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.3",
133
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-alpha.3",
134
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.3",
135
+ "@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.3",
136
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.3",
137
+ "@deepseek-ai/dsh-ptc-runtime": "0.2.1-alpha.3",
138
+ "@deepseek-ai/dsh-sandbox": "0.2.1-alpha.3",
139
+ "@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.3",
140
+ "@deepseek-ai/dsh-scope": "0.2.1-alpha.3",
141
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.3",
142
+ "@deepseek-ai/dsh-session-log-export": "0.2.1-alpha.3",
143
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.3",
144
+ "@deepseek-ai/dsh-session-query": "0.2.1-alpha.3",
145
+ "@deepseek-ai/dsh-session-stats": "0.2.1-alpha.3",
146
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.3",
147
+ "@deepseek-ai/dsh-timeout": "0.2.1-alpha.3",
148
+ "@deepseek-ai/dsh-token-meter": "0.2.1-alpha.3",
149
+ "@deepseek-ai/dsh-tools": "0.2.1-alpha.3",
150
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.3",
151
+ "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.3",
152
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.3",
153
+ "@deepseek-ai/dsh-workspace": "0.2.1-alpha.3",
154
154
  "@deepseek-ai/schemastery": "3.18.4",
155
155
  "@types/node": "^26.2.0",
156
156
  "@types/react": "~18.3.1",
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * The dsh-better-sidebar adapter (PB-12), written against EXACTLY
3
- * version 0.25.0. Everything it knows about the plugin came off
4
- * `/tmp/bs-versions/0.25.0/lib/index.js` (the installed copy at
5
- * `~/.dsh/profiles/desktop/node_modules/dsh-better-sidebar` is the same
6
- * build); the method table lives in `methods.ts` beside this file, and the
7
- * test suite re-extracts the plugin's real method-name set to catch a
3
+ * versions 0.25.0 and 0.26.0 (PB-15 widened the list; 0.26.0 is a JSON-api
4
+ * superset of 0.25.0 — it grows fs.readPage and the sidechat dequeue/model/
5
+ * models methods, and moves fs.write's content onto the new raw-text
6
+ * /sidebar/write route). Everything the adapter knows about the plugin came
7
+ * off `/tmp/bs-versions/{0.25.0,0.26.0}/lib/index.js`; the method table
8
+ * lives in `methods.ts` beside this file, and the test suite re-extracts
9
+ * the plugin's real method-name set (the two-version UNION) to catch a
8
10
  * silently drifted version.
9
11
  *
10
12
  * Product decisions (2026-10-10): reads AND writes both forward — a
@@ -12,15 +14,18 @@
12
14
  * model spend (commit-message generation), documented as such; NO path
13
15
  * fencing; `open.external` refuses for a remote session; `settings.*` and
14
16
  * `/sidebar/bundle` stay local; the adapter registers as whole-machine
15
- * data (consent per plugin, default off).
17
+ * data (consent per plugin, default off). PB-15: the 0.26.0 WebSocket
18
+ * `/sidebar/ws/sidechat` is deliberately NOT declared (not bridged).
16
19
  *
17
20
  * The `cwd` handling is the one subtle part: better-sidebar resolves a
18
21
  * request's working directory as the session's own `header.cwd` first and
19
- * only falls back to a `cwd` field the CLIENT supplied (lib/index.js:6295,
20
- * 6075-6099). A remote panel must not steer the server by path, so the
21
- * server hook strips `cwd` — from the JSON body (`/sidebar/api`) and from
22
- * the query (`/sidebar/file`, `/sidebar/upload`) — and lets the backend
23
- * resolve from the (validated, real) session id.
22
+ * only falls back to a `cwd` field the CLIENT supplied (0.25.0
23
+ * lib/index.js:6295, 6075-6099; 0.26.0 resolves /sidebar/write's cwd the
24
+ * same way, lib/index.js:7902). A remote panel must not steer the server
25
+ * by path, so the server hook strips `cwd` — from the JSON body
26
+ * (`/sidebar/api`) and from the query (`/sidebar/file`, `/sidebar/upload`,
27
+ * `/sidebar/write`) — and lets the backend resolve from the (validated,
28
+ * real) session id.
24
29
  */
25
30
 
26
31
  import type { BridgeAdapter } from '../types.js'
@@ -44,7 +49,7 @@ const apiMethodOf = (pathname: string): string => sidebarMethodNameOf(pathname)
44
49
 
45
50
  export const dshBetterSidebarAdapter: BridgeAdapter = {
46
51
  plugin: 'dsh-better-sidebar',
47
- versions: ['0.25.0'],
52
+ versions: ['0.25.0', '0.26.0'],
48
53
  routes: [
49
54
  {
50
55
  // The JSON API: the method name rides the path tail, the session
@@ -82,6 +87,13 @@ export const dshBetterSidebarAdapter: BridgeAdapter = {
82
87
  // The raw upload (lib/index.js:6905-6948): binary bytes, capped by the
83
88
  // relay's own upload budget.
84
89
  { path: '/sidebar/upload', match: 'exact', methods: ['POST'], maxBody: RELAY_UPLOAD_CAP_BYTES, locator: { in: 'query', name: 'sessionId' } },
90
+ // The raw text write (0.26.0, lib/index.js:7875-7919): the document IS
91
+ // the POST body (text/plain — no JSON escaping layer), the session id,
92
+ // path and a client cwd ride the QUERY. The plugin caps the body at
93
+ // WRITE_BODY_GUARD_BYTES = 8 MiB (lib/index.js:6854); the adapter takes
94
+ // 10 MiB — headroom over the plugin's own guard, under the relay's
95
+ // 100 MiB upload budget.
96
+ { path: '/sidebar/write', match: 'exact', methods: ['POST'], maxBody: 10 * 1024 * 1024, locator: { in: 'query', name: 'sessionId' } },
85
97
  // The HTML preview (lib/index.js:7026+): the session id is the FIRST
86
98
  // path segment after the prefix; the rest of the segments are the
87
99
  // preview file's relative path. Same-prefix relative resources ride
@@ -125,7 +137,7 @@ export const dshBetterSidebarAdapter: BridgeAdapter = {
125
137
  // directory from the (already validated) session id's own header,
126
138
  // falling back to the persisted record — a remote panel never
127
139
  // steers the server by path.
128
- if (ctx.pathname === '/sidebar/file' || ctx.pathname === '/sidebar/upload') {
140
+ if (ctx.pathname === '/sidebar/file' || ctx.pathname === '/sidebar/upload' || ctx.pathname === '/sidebar/write') {
129
141
  if (ctx.query.get('cwd') !== null) {
130
142
  const stripped = new URLSearchParams(ctx.query)
131
143
  stripped.delete('cwd')
@@ -1,10 +1,19 @@
1
1
  /**
2
- * better-sidebar 0.25.0 的 `/sidebar/api` 方法表(PB-12)——适配器唯一
3
- * 认识的接口清单,逐条对着 `/tmp/bs-versions/0.25.0/lib/index.js` 复核
4
- * (行号见各条注释,以 0.25.0 的构建产物为准)。表里没有的方法在远程
5
- * 会话里一律拒绝(reason `adapter-method-unknown`):宁可少转发,不可
6
- * 误转发——`test/adapter-better-sidebar.test.cjs` 会把源码里的实际方法
7
- * 名集合提取出来与这张表逐一比对,防止插件升级后漏记。
2
+ * better-sidebar 0.25.0 + 0.26.0 的 `/sidebar/api` 方法表(PB-12/PB-15)
3
+ * ——适配器唯一认识的接口清单,逐条对着 `/tmp/bs-versions/{0.25.0,
4
+ * 0.26.0}/lib/index.js` 复核(行号见各条注释,未标版本的一律以 0.25.0
5
+ * 的构建产物为准;0.26.0 是 0.25.0 的 JSON-api 超集,除下述三处外形状
6
+ * 不变)。表里没有的方法在远程会话里一律拒绝(reason
7
+ * `adapter-method-unknown`):宁可少转发,不可误转发——
8
+ * `test/adapter-better-sidebar.test.cjs` 会把两版源码里的实际方法名集合
9
+ * 提取出来求并集,与这张表逐一比对,防止插件升级后漏记。
10
+ *
11
+ * 0.26.0 相对 0.25.0 的差异(本表如何覆盖两版):
12
+ * - 新增 `fs.readPage` 与 sidechat 的 `dequeue`/`model`/`models` 三个;
13
+ * - `fs.write` 从 JSON api 移除,改走新的 `/sidebar/write` HTTP 路由
14
+ * (内容作纯文本 POST 体,见 index.ts 的路由条目)。表里保留
15
+ * `fs.write`:0.25.0 的宿主仍从 JSON api 派发它;0.26.0 的客户端
16
+ * 不再调它,登记无害。
8
17
  *
9
18
  * 会话字段:绝大多数方法在 JSON 体顶层带 `sessionId`(client.js 的
10
19
  * scopePayload,lib/client.js:3866);子代理与工作流面板带
@@ -70,7 +79,7 @@ export interface SidebarMethodSpec {
70
79
  /** 无改写的普通转发方法的简写工厂。 */
71
80
  const forward = (kind: 'read' | 'write', sessionField: 'sessionId' | 'rootSessionId' | 'childId' = 'sessionId'): SidebarMethodSpec => ({ kind, sessionField })
72
81
 
73
- /** 0.25.0 的全部方法。 */
82
+ /** 两版(0.25.0 + 0.26.0)方法名的并集,各条目的处置见上。 */
74
83
  export const SIDEBAR_METHODS: Readonly<Record<string, SidebarMethodSpec>> = {
75
84
  // ---- 会话(lib/index.js:6319-6333)----
76
85
  'session.phase': forward('read'),
@@ -86,6 +95,11 @@ export const SIDEBAR_METHODS: Readonly<Record<string, SidebarMethodSpec>> = {
86
95
  'fs.trees': forward('read'),
87
96
  'fs.search': forward('read'),
88
97
  'fs.read': forward('read'),
98
+ // 分页读(0.26.0 lib/index.js:7332-7338):payload 形状同 fs.read 加
99
+ // 顶层 `offset` 数字;响应是文件文本,不带会话 id —— 不改写。
100
+ 'fs.readPage': forward('read'),
101
+ // 0.26.0 起从 JSON api 移除(写内容改走 /sidebar/write 路由,见
102
+ // index.ts);保留给 0.25.0 的宿主——0.26.0 的客户端不再派发它。
89
103
  'fs.write': forward('write'),
90
104
  'fs.rename': forward('write'),
91
105
  'fs.mkdir': forward('write'),
@@ -171,6 +185,15 @@ export const SIDEBAR_METHODS: Readonly<Record<string, SidebarMethodSpec>> = {
171
185
  'sidechat.dispose': forward('write', 'childId'),
172
186
  'sidechat.info': forward('read', 'childId'),
173
187
  'sidechat.events': forward('read', 'childId'),
188
+ // 撤回排队消息(0.26.0 lib/index.js:6206-6226):childId 定位,可选
189
+ // `messageId`;响应 {accepted, removed} 不带会话 id —— 不改写。
190
+ 'sidechat.dequeue': forward('write', 'childId'),
191
+ // 模型目录(0.26.0 lib/index.js:6228):handler 不收 payload、无会话
192
+ // 字段 → local(settings.* 先例),登记只为「未知方法」比对。
193
+ 'sidechat.models': { kind: 'read', local: true },
194
+ // 给线程切模型(0.26.0 lib/index.js:6229-6245):childId 定位;响应
195
+ // {accepted, selection} 不带会话 id —— 不改写。
196
+ 'sidechat.model': forward('write', 'childId'),
174
197
  }
175
198
 
176
199
  /** `/sidebar/api` 的路径前缀(方法名跟在后面,lib/index.js:6888)。 */
@@ -37,6 +37,7 @@
37
37
  * drive it over a real socket with a mock gateway and a mock admit.
38
38
  */
39
39
 
40
+ import { withPeer } from './owner-peer.js'
40
41
  import { request as httpRequest } from 'node:http'
41
42
  import type { IncomingMessage, ServerResponse } from 'node:http'
42
43
  import type { ConfigSource, ZenRemoteConfig } from './config.js'
@@ -150,6 +151,8 @@ export interface AdminHandlerOptions {
150
151
  * share trusts the table alone. The lookup itself must never be allowed to
151
152
  * throw into the route, so callers wrap it. */
152
153
  typert?: () => RelayGateway | undefined
154
+ /** The owner Peer gateway calls are made as (DSH 0.2.1-alpha.3+); see owner-peer.ts. */
155
+ getPeer?: () => unknown
153
156
  /** Live agent roster for the busy restore a successful `share` performs.
154
157
  * Absent or throwing means "no information": nothing is marked busy. */
155
158
  listAgents?: () => readonly AgentStatusLike[]
@@ -358,12 +361,12 @@ async function projectionOf(options: AdminHandlerOptions, sessionId: string): Pr
358
361
  return { kind: 'no-gateway' }
359
362
  }
360
363
  if (gateway === undefined) return { kind: 'no-gateway' }
361
- const value = await gateway.invoke({
364
+ const value = await gateway.invoke(withPeer({
362
365
  namespace: 'session',
363
366
  method: 'projections',
364
367
  args: { request: { sessionId } },
365
368
  signal: AbortSignal.timeout(options.projectionTimeoutMs ?? PROJECTION_TIMEOUT_MS),
366
- })
369
+ }, options.getPeer))
367
370
  if (value === null || value === undefined) return { kind: 'no-session' }
368
371
  const values = (value as { values?: unknown }).values
369
372
  return {
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  * the package.json dsh.client declaration.
16
16
  */
17
17
 
18
+ import { createOwnerPeerSource } from './owner-peer.js'
18
19
  import { lstat, mkdir, open, realpath, rm } from 'node:fs/promises'
19
20
  import type { FileHandle } from 'node:fs/promises'
20
21
  import type { IncomingMessage, ServerResponse } from 'node:http'
@@ -715,6 +716,7 @@ export function apply(ctx: Context, config: MobileNavConfig = {}): void {
715
716
  // which would turn every shares request into a 500.
716
717
  store,
717
718
  typert: () => webCtx.reflect.get('typertGateway') as RelayGateway | undefined,
719
+ getPeer: createOwnerPeerSource(() => webCtx.connection),
718
720
  listAgents: () => (webCtx.reflect.get('agents') as { list(): readonly AgentStatusLike[] } | undefined)?.list() ?? [],
719
721
  // The "who is looking" badge (T33a2): read through whichever relay
720
722
  // handler is live right now. sessionQuery likewise — the share
@@ -780,6 +782,7 @@ export function apply(ctx: Context, config: MobileNavConfig = {}): void {
780
782
  // registry and pin those throwaway values for the row's lifetime.
781
783
  // ~7 ms per computation against a full registry; a failed compute
782
784
  // lands as the empty map inside the relay handler's own guard.
785
+ const relayPeer = createOwnerPeerSource(() => relayCtx.reflect.get('connection'))
783
786
  relayCtx.effect(() => {
784
787
  // The plugin bridge's loopback facts (PB-3), both read PER REQUEST
785
788
  // through the reflection layer: the web server may have restarted on
@@ -812,6 +815,10 @@ export function apply(ctx: Context, config: MobileNavConfig = {}): void {
812
815
  store,
813
816
  gateway: gatewayService,
814
817
  getApiFetch,
818
+ // DSH 0.2.1-alpha.3: one cached owner/answer Peer for every relay
819
+ // gateway call — `$events/result` must come from the Peer that
820
+ // opened `$events`.
821
+ getPeer: relayPeer,
815
822
  // Subagent reachability (T22c-fix): isAccessible walks the same
816
823
  // child→parent chain the activity tracker keeps fresh.
817
824
  parentOf: parentIndex.parentOf,
@@ -1068,7 +1075,15 @@ export function apply(ctx: Context, config: MobileNavConfig = {}): void {
1068
1075
  // second failure uninstalls (a half-trusted interception is worse than
1069
1076
  // none). Failure handling lives in runSelfCheck, which records the
1070
1077
  // verdict via noteSelfCheck.
1071
- void runSelfCheck(raw, { handle, log: logWarn })
1078
+ // DSH 0.2.1-alpha.3: the probe needs an owner Peer of its own (the
1079
+ // gateway's operatorPeer fallback is gone) — read `connection` through
1080
+ // the reflection layer so a composition without it still installs.
1081
+ void runSelfCheck(raw, {
1082
+ handle,
1083
+ log: logWarn,
1084
+ info: (format, ...args) => { ctx.logger?.info(`dsh-zen-remote ${format}`, ...args) },
1085
+ getPeer: createOwnerPeerSource(() => gatewayCtx.reflect.get('connection')),
1086
+ })
1072
1087
  })
1073
1088
  // The exact-fetch-route interception (T51): the local backend's
1074
1089
  // `/api/session/uploadFileBinary` entry forwards REMOTE sessions'
@@ -23,6 +23,11 @@ export type GatewayShapeCheck = { ok: true; notes: string[] } | { ok: false; rea
23
23
  * §2.2/§4.1). If these strings vanish from the constructor source, own
24
24
  * properties would silently stop intercepting. */
25
25
  const DYNAMIC_OPEN_CALL = 'this.openWireStream(endpoint, payload, uplink, peer, control.signal, control)'
26
+ /** DSH 0.2.1-alpha.3 threads the mux's per-stream uplink window through as a
27
+ * seventh argument (`uplinkWindow`); the call site is otherwise unchanged. */
28
+ const DYNAMIC_OPEN_CALL_WINDOW = 'this.openWireStream(endpoint, payload, uplink, peer, control.signal, control, window)'
29
+ /** Accepted `openWireStream` arities: 6 through 0.2.1-alpha.2, 7 from alpha.3. */
30
+ const OPEN_ARITIES = [6, 7]
26
31
  const DYNAMIC_RPC_CALL = 'this.dispatchRpc(endpoint, payload, signal, peer)'
27
32
 
28
33
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -48,7 +53,9 @@ export function checkGatewayShape(raw: unknown): GatewayShapeCheck {
48
53
  // already installed.
49
54
  const open = proto === null ? undefined : proto.openWireStream
50
55
  if (typeof open !== 'function') reasons.push('prototype: openWireStream is not a function')
51
- else if (open.length !== 6) reasons.push(`prototype: openWireStream has ${open.length} parameters, expected 6`)
56
+ else if (!OPEN_ARITIES.includes(open.length)) {
57
+ reasons.push(`prototype: openWireStream has ${open.length} parameters, expected ${OPEN_ARITIES.join(' or ')}`)
58
+ }
52
59
  const dispatch = proto === null ? undefined : proto.dispatchRpc
53
60
  if (typeof dispatch !== 'function') reasons.push('prototype: dispatchRpc is not a function')
54
61
  else if (dispatch.length !== 4) reasons.push(`prototype: dispatchRpc has ${dispatch.length} parameters, expected 4`)
@@ -65,8 +72,8 @@ export function checkGatewayShape(raw: unknown): GatewayShapeCheck {
65
72
  if (constructorSource === '') {
66
73
  reasons.push('constructor: the prototype has no constructor to inspect')
67
74
  } else {
68
- if (!constructorSource.includes(DYNAMIC_OPEN_CALL)) {
69
- reasons.push(`constructor: source does not contain "${DYNAMIC_OPEN_CALL}"`)
75
+ if (!constructorSource.includes(DYNAMIC_OPEN_CALL) && !constructorSource.includes(DYNAMIC_OPEN_CALL_WINDOW)) {
76
+ reasons.push(`constructor: source does not contain "${DYNAMIC_OPEN_CALL}" or "${DYNAMIC_OPEN_CALL_WINDOW}"`)
70
77
  }
71
78
  if (!constructorSource.includes(DYNAMIC_RPC_CALL)) {
72
79
  reasons.push(`constructor: source does not contain "${DYNAMIC_RPC_CALL}"`)