@deepseek-ai/dsh-experimental-liveshare 0.0.0-stage → 0.2.2-alpha.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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +52 -0
  3. package/README.md +176 -2
  4. package/README.zh.md +177 -0
  5. package/cordis.patch.yml +17 -0
  6. package/icon.svg +12 -0
  7. package/lib/client.js +7077 -0
  8. package/lib/index.js +7778 -0
  9. package/lib/owner-gateway/cli.js +370 -0
  10. package/lib/retry-backoff-ccJYtzOj.js +121 -0
  11. package/lib/typert.host.d.ts +3 -0
  12. package/lib/typert.host.js +767 -0
  13. package/lib/typert.remote-client.d.ts +58 -0
  14. package/lib/typert.remote-client.js +551 -0
  15. package/lib/types/client/ManageSharesDialog.d.ts +21 -0
  16. package/lib/types/client/ShareAction.d.ts +19 -0
  17. package/lib/types/client/ShareCapabilitiesPicker.d.ts +77 -0
  18. package/lib/types/client/ShareCreateForm.d.ts +104 -0
  19. package/lib/types/client/ShareDialog.d.ts +28 -0
  20. package/lib/types/client/ShareEditPanel.d.ts +98 -0
  21. package/lib/types/client/SharePermissionFields.d.ts +46 -0
  22. package/lib/types/client/endpoint-classification.d.ts +204 -0
  23. package/lib/types/client/index.d.ts +33 -0
  24. package/lib/types/client/locales.d.ts +282 -0
  25. package/lib/types/client/owner-display.d.ts +41 -0
  26. package/lib/types/client/owner-identity-login.d.ts +72 -0
  27. package/lib/types/client/owner-identity.d.ts +81 -0
  28. package/lib/types/client/owner-login.d.ts +146 -0
  29. package/lib/types/client/owner-routes.d.ts +20 -0
  30. package/lib/types/client/owner-shares.d.ts +83 -0
  31. package/lib/types/client/share-navigation.d.ts +25 -0
  32. package/lib/types/client/share-options.d.ts +19 -0
  33. package/lib/types/client/share-relay-address.d.ts +117 -0
  34. package/lib/types/client/share-types.d.ts +76 -0
  35. package/lib/types/client/user-origin.d.ts +46 -0
  36. package/lib/types/client/viewer-brand.d.ts +48 -0
  37. package/lib/types/client/viewer-capabilities.d.ts +74 -0
  38. package/lib/types/client/viewer-context.d.ts +45 -0
  39. package/lib/types/client/viewer-model.d.ts +77 -0
  40. package/lib/types/client/viewer-permission.d.ts +44 -0
  41. package/lib/types/client/viewer-seat.d.ts +55 -0
  42. package/lib/types/client/viewer-session-plugin.d.ts +47 -0
  43. package/lib/types/client/viewer-session-row.d.ts +21 -0
  44. package/lib/types/client/viewer-session.d.ts +20 -0
  45. package/lib/types/client/viewer-share-path.d.ts +32 -0
  46. package/lib/types/client/viewer-status-banner.d.ts +33 -0
  47. package/lib/types/client/viewer-status.d.ts +69 -0
  48. package/lib/types/endpoint-classification.d.ts +236 -0
  49. package/lib/types/fork-lineage.d.ts +47 -0
  50. package/lib/types/host/base64-url.d.ts +17 -0
  51. package/lib/types/host/fork-ceiling.d.ts +41 -0
  52. package/lib/types/host/fork-enforcement.d.ts +271 -0
  53. package/lib/types/host/fork-policy.d.ts +147 -0
  54. package/lib/types/host/fork-registry.d.ts +231 -0
  55. package/lib/types/host/fork-tool-guard.d.ts +148 -0
  56. package/lib/types/host/owner-authorization.d.ts +128 -0
  57. package/lib/types/host/owner-credential-file.d.ts +64 -0
  58. package/lib/types/host/owner-http-routes.d.ts +46 -0
  59. package/lib/types/host/owner-persistence.d.ts +91 -0
  60. package/lib/types/host/owner-share-operations.d.ts +70 -0
  61. package/lib/types/host/owner-types.d.ts +64 -0
  62. package/lib/types/host/relay-authority.d.ts +130 -0
  63. package/lib/types/host/relay-url.d.ts +16 -0
  64. package/lib/types/host/share-host.d.ts +61 -0
  65. package/lib/types/host/share-operations.d.ts +87 -0
  66. package/lib/types/host/share-remote.d.ts +96 -0
  67. package/lib/types/host/shared-tools.d.ts +109 -0
  68. package/lib/types/host/source-access.d.ts +171 -0
  69. package/lib/types/host/tunnel-supervision.d.ts +218 -0
  70. package/lib/types/host/tunnel-worker.d.ts +80 -0
  71. package/lib/types/host/viewer-access-policy.d.ts +100 -0
  72. package/lib/types/host/viewer-assets.d.ts +23 -0
  73. package/lib/types/host/viewer-capability-selection.d.ts +59 -0
  74. package/lib/types/host/viewer-declarations.d.ts +162 -0
  75. package/lib/types/host/viewer-dispatch.d.ts +88 -0
  76. package/lib/types/host/viewer-fork.d.ts +84 -0
  77. package/lib/types/host/viewer-lifecycle.d.ts +226 -0
  78. package/lib/types/host/viewer-principal.d.ts +59 -0
  79. package/lib/types/host/viewer-read.d.ts +201 -0
  80. package/lib/types/host/wire-json.d.ts +24 -0
  81. package/lib/types/index.d.ts +15 -0
  82. package/lib/types/owner-gateway/cli.d.ts +3 -0
  83. package/lib/types/owner-gateway/pending-rpc.d.ts +47 -0
  84. package/lib/types/owner-gateway/registration-url.d.ts +20 -0
  85. package/lib/types/owner-gateway/relay-refusal.d.ts +28 -0
  86. package/lib/types/owner-gateway/retry-backoff.d.ts +37 -0
  87. package/lib/types/owner-gateway/worker.d.ts +7 -0
  88. package/lib/types/owner-identity.d.ts +23 -0
  89. package/lib/types/read-budgets.d.ts +45 -0
  90. package/lib/types/share-api-types.d.ts +284 -0
  91. package/lib/types/viewer-channel.d.ts +107 -0
  92. package/lib/types/viewer-declarations.d.ts +22 -0
  93. package/lib/viewer-declarations-krUNq6VU.js +287 -0
  94. package/lib/viewer-declarations.js +26 -0
  95. package/locale/de.json +6 -0
  96. package/locale/en.json +6 -0
  97. package/locale/es.json +6 -0
  98. package/locale/fr.json +6 -0
  99. package/locale/ja.json +6 -0
  100. package/locale/pt.json +6 -0
  101. package/locale/ru.json +6 -0
  102. package/locale/zh.json +6 -0
  103. package/package.json +169 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/experimental/liveshare/README.md
5
+ /:
6
+ en: 01889c844628a948
7
+ zh: 94366d9239f1c383
8
+ /deepseek-ai-dsh-experimental-liveshare:
9
+ en: d922aebfcdd341f4
10
+ zh: 92b631fd849b6d03
11
+ /deepseek-ai-dsh-experimental-liveshare/summary:
12
+ en: 5079cf4c5eb1d15a
13
+ zh: 96b21dd402dfae5c
14
+ /deepseek-ai-dsh-experimental-liveshare/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 405b30feaf48c087
17
+ /deepseek-ai-dsh-experimental-liveshare/use-this-package:
18
+ en: bbff250eba0f1b89
19
+ zh: 0a618f05cc88ee2b
20
+ /deepseek-ai-dsh-experimental-liveshare/use-this-package/profile-and-build-requirements:
21
+ en: 86aa723d77827dca
22
+ zh: 3cb8774c8c7b1b8c
23
+ /deepseek-ai-dsh-experimental-liveshare/use-this-package/default-visibility-and-enabling:
24
+ en: fe4942f55344093e
25
+ zh: dbc07edfe1d702d9
26
+ /deepseek-ai-dsh-experimental-liveshare/use-this-package/owner-configuration:
27
+ en: 42b24e06e416eb1d
28
+ zh: 45f7e2003eb3ae2f
29
+ /deepseek-ai-dsh-experimental-liveshare/use-this-package/sign-in-and-manage-shares:
30
+ en: a23d0162e0efa6a4
31
+ zh: 0d01659cfeb94640
32
+ /deepseek-ai-dsh-experimental-liveshare/use-this-package/session-tiers:
33
+ en: f8d810298790cd3a
34
+ zh: 95fcec905f8d41d5
35
+ /deepseek-ai-dsh-experimental-liveshare/understand-the-implementation:
36
+ en: 7620555e5da0a2e9
37
+ zh: 3c40fc8b97004fda
38
+ /deepseek-ai-dsh-experimental-liveshare/further-exploration:
39
+ en: 0570170704bbe5b6
40
+ zh: 832f86024b26e5b3
41
+ /deepseek-ai-dsh-experimental-liveshare/model-experience:
42
+ en: 3b248c02220df02d
43
+ zh: 9d752fb049a04e83
44
+ /deepseek-ai-dsh-experimental-liveshare/model-experience/kv-cache-effect:
45
+ en: 22a9da7d4b23a015
46
+ zh: 5700901ba5f655c8
47
+ /deepseek-ai-dsh-experimental-liveshare/known-limitations-and-deferred-work:
48
+ en: 6c4107962a32a8e7
49
+ zh: 2f4d7970b1ac90de
50
+ /deepseek-ai-dsh-experimental-liveshare/known-limitations-and-deferred-work/dev-note:
51
+ en: e67f633dc6fe7773
52
+ zh: 8ec3e73528ddb1a6
package/README.md CHANGED
@@ -1,3 +1,177 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "Share DSH Sessions through a separate Liveshare server, with verified owner sign-in, viewer controls, and bounded execution."
3
+ kind: "package-bundle"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @deepseek-ai/dsh-experimental-liveshare
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ Share a Session with colleagues through the normal DSH browser interface. The owner chooses viewing or bounded writing, and independently allows forking; the owner host enforces those choices. Attachment upload follows sharing itself: a Session the viewer can see accepts files. This opt-in bundle adds owner sign-in, sharing controls, the outgoing gateway, and the viewer surfaces of the owner's own Web page. The default Web profile excludes it. The separately deployed Liveshare server authenticates viewers and transports their requests; its OAuth, database, routing, and deployment implementation are not part of this package.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ ### Profile and build requirements
29
+
30
+ The manifest declares an installable `dsh.bundle` layer and a browser face. The bundle stays hidden until a profile enables it, as described in [Default visibility and enabling](#default-visibility-and-enabling); supported applications still start through `dsh --profile`.
31
+
32
+ Owner, viewer, and the Liveshare server must use matching protocol versions. The build includes the Owner's gateway process; the viewer page has no separate build. Package version, exports, and publication contents are declared in [package.json](package.json).
33
+
34
+ <a id="default-visibility-and-enabling"></a>
35
+
36
+ ### Default visibility and enabling
37
+
38
+ A dsh installation ships this package as a runtime dependency, but no shipped profile selects its bundle, so none of its plugins run and the plugin manager lists it in neither **Built-in plugin settings** nor **Experimental plugins**, including the **More** page. The bundle ID stays addressable, so a page that opens a bundle by ID still reaches this bundle's details.
39
+
40
+ Enable it by inserting its two entries into the active profile's patch file, `$DSH_HOME/profiles/<profile>/cordis.patch.yml`. That file is one YAML sequence of Loader patches; these are the entries the bundle's own [patch](cordis.patch.yml) inserts, and `!!js` expressions are allowed there:
41
+
42
+ ```yaml
43
+ - insert:
44
+ - id: liveshare-viewer-declarations
45
+ name: '@deepseek-ai/dsh-experimental-liveshare/viewer-declarations'
46
+ - id: liveshare-share-host
47
+ name: '@deepseek-ai/dsh-experimental-liveshare'
48
+ config:
49
+ ownerIdentityProvider: <provider-id>
50
+ ownerIdentityTenant: <tenant-id>
51
+ ownerIdentitySubject: <subject-id>
52
+ allowedSubjectIds: []
53
+ ownerStateFile: /path/to/owner-shares.json
54
+ ```
55
+
56
+ Replace every `<...>` placeholder with the deployment's own value: the identity provider, tenant, and subject are the ones its Liveshare server deployment was configured with, and the example holdings are placeholders rather than real identities. The environment layer works too: set `LIVESHARE_*` in `$DSH_HOME/.env` and keep the bundle's own `!!js process.env.…` expressions. The Liveshare server address is not part of this configuration: the owner enters it in the Share dialog on first use, as Owner configuration below describes.
57
+
58
+ The profile can select the bundle instead of patching it: add `"@deepseek-ai/dsh-experimental-liveshare"` to `dsh.profile.bundles` in `$DSH_HOME/profiles/<profile>/package.json`, which is the write the card's switch performs once the card is listed.
59
+
60
+ Either route takes effect only on a recomposition. With the profile's HMR row enabled, the change applies when the profile is re-read; without it, the next `dsh` start applies it. Once at least one row the bundle declares runs, its card appears under **Experimental plugins** and on the **More** page. Removing the entries, disabling them, or removing the bundle from `dsh.profile.bundles` stops those rows and the card leaves both lists again.
61
+
62
+ ### Owner configuration
63
+
64
+ | Variable | Default | Meaning |
65
+ |---|---|---|
66
+ | `LIVESHARE_OWNER_PROVIDER_ID` / `LIVESHARE_OWNER_TENANT_ID` / `LIVESHARE_OWNER_SUBJECT_ID` | unset | Owner identity used for legacy subject matching; it does not replace a server-issued credential |
67
+ | `LIVESHARE_OWNER_DISPLAY_NAME` | unset | Display label for the configured identity |
68
+ | `LIVESHARE_ALLOWED_SUBJECTS` | empty | Deployment-wide subject allowlist in addition to the share's access rules |
69
+ | `LIVESHARE_OWNER_STATE_FILE` | `$DSH_HOME/liveshare/owner-shares.json` | Stored share metadata, the saved Liveshare server address, instance identity, and management tokens |
70
+ | `LIVESHARE_OWNER_CREDENTIAL_FILE` | `owner-credential.json` beside the state file | Stored credential bound to the Liveshare server that issued it |
71
+
72
+ The Liveshare server address is set on first use: enter it in the Share dialog's address field and save it. The saved address persists in the owner state file, and later shares publish to it; a publication already waiting for registration keeps its starting server address in its registration, stored record, and returned link, and existing shares retain their recorded Liveshare server address. Clearing the saved address through the share-management Remote leaves this host without one — creating a share then reports that an address has to be saved — rather than falling back to a deployment value. The name a reader sees beside a share comes from the display name on the Relay-verified credential, and local configuration cannot override it; a credential that carries only a subject shows that subject's label. Provider application secrets belong to the Liveshare server, not this configuration.
73
+
74
+ ### Sign in and manage shares
75
+
76
+ The Share dialog starts a short-lived authorization transaction on the Owner. A popup returns a single-use code only to the expected window, origin, and transaction. The Owner redeems it with its private verifier and saves the credential bound to that Liveshare server; the normal browser login flow never stores that credential in browser storage or a URL. Expired, cancelled, replayed, or mismatched transactions fail closed. Changing the Liveshare server during code redemption, including a change away and back, refuses login before credential storage. After storage commits, probes and share claims retain that operation’s Liveshare server and credential identity. Publishing without verified identity is refused, including when the Liveshare server does not support identity binding.
77
+
78
+ Share management is the `liveshareShares` Typert Remote: the share list, creation, stopping, tier and fork changes, endpoint selection, the Liveshare server state, the owner identity, the audit trail the Liveshare server holds, and both Liveshare server checks are methods on the mounted namespace. The sign-in round trip and credential storage are the only same-origin HTTP routes the owner page still calls.
79
+
80
+ The owner admits viewers by organization link; the deployment's `LIVESHARE_ALLOWED_SUBJECTS` gates every share and narrows an organization-link share to the subjects it lists. Stored `selected` or `approval` shares keep their original ACL and access summary, and the dialog no longer creates them; `liveshareShares.create` keeps `accessMode` and `allowedSubjectIds` for those records and direct Remote callers. Manage Shares lists stored links, tiers, availability, and the Liveshare server's audit responses, and it can change a share's tier, turn its fork switch on or off, and stop a share. Failed operations remain visible as errors; the UI re-reads the Owner's response rather than treating a click as success. The Share dialog edits an existing share too: its tier, fork switch, and plugin endpoints are picked there and submitted by one save that sits outside the scrolling fields and re-reads the Owner's record; unsaved picks are stated, and closing first warns that they would be discarded.
81
+
82
+ ### Session tiers
83
+
84
+ | Tier | Viewer actions | Execution limit |
85
+ |---|---|---|
86
+ | `view` | Prompt the source Session and stop its running turn | Read-only sandbox mounted on the Session, applied to every turn a share viewer starts: that turn may use the read and search surface (`read`, `read_image`, `glob`, `grep`, `present`), the tools with no local file effect (`todo_write`, `skill`, `web_search`, `web_fetch`); every path a call declares is checked against the granted workspace |
87
+ | `write` | Also modify files inside that workspace | Writable sandbox with limits: the view surface plus bounded `edit` and `write` inside the granted workspace, for every viewer-started turn of the Session |
88
+
89
+ Both tiers drive the shared Session; what separates them is writing. The sandbox is mounted on the Session for as long as any share over it is active, but it confines only the turns a share viewer starts: a turn the owner starts keeps the Session's own tools, including `bash` and its unrestricted filesystem tools, and an owner's steering input inside a viewer's turn stays confined with it. The decision is kept in Host memory per turn and never written to the Session log; stopping every share or letting them all expire releases the layer and restores the Session's own tools. Forking is not a tier: the share carries its own switch, independent of the tier, and a viewer it admits forks a completed source prefix into a child that runs under the read-only ceiling, or under the bounded-write ceiling when the share is at the `write` tier.
90
+
91
+ Attachment uploads are neither a tier nor part of the fork switch: they follow visibility. An admitted viewer may attach uploaded files to the shared Session and to the forks it created through this share, whichever tier the share carries and whether or not it allows forking. The owner has no separate attachment switch.
92
+
93
+ No tier grants Session configuration, renaming, archival, Workspace management, or the owner's global settings and credentials; attachment uploads follow visibility and are not a tier grant. The Owner Gateway refuses a viewer's `settings/describe`, `credentials/describe`, `agentPresets/list`, `agentPresets/read`, and every other endpoint the share does not grant with the Remote failure `remote-access/denied` (reason `liveshare/read-only`); configuration values, credential-reference metadata, and preset plugin configuration are not viewer data. The viewer keeps its local presentation defaults. Those management decisions stay independent from permission to prompt a Session. **Temporary:** a plugin that owns an endpoint this page needs offers it through [`viewerDeclarationsOf(ctx)`](src/host/viewer-declarations.ts) — `registerViewerReadable(actions)` for endpoints that change nothing, `registerViewerWritable(actions)` for endpoints that write — until endpoint-level access declarations in Typert metadata replace this surface. **An offer grants nothing by itself**: the owner exposes a plugin's endpoints one share at a time, and a share exposes an endpoint only where that plugin's live offer and that share's own selection name it, on the resources the reader can see. A writable selection rides the share's own write rule, so **it takes effect only in a share whose Session already allows writes and whose source execution is confined, and no offer and no selection turns a read-only share into a writable one**; either kind leaves the actions this policy already decides — `session/prompt`, `session/cancel`, `session/fork`, and the rest of its roles — to the mode's own answer. The owner's own page opens with the owner access baseline, so its interface is unchanged.
94
+
95
+ The Owner Gateway decides each call on the Session and Workspace it names and refuses a call whose target is missing or invalid. The viewer's Session list and workspace membership are trimmed to the source and the children that viewer owns. The page reads each Session's own answer from the reader's access snapshot, so a selection change never carries the previous Session's permission across. Unknown or unreadable modes fail closed. The Owner rechecks admission for source and child calls; revocation cancels the affected calls and streams, and a share whose admission narrows stops the shared Session's running turn. The page distinguishes authentication, access, expiry, stopped shares, offline owners, and transport failures, and retries only the failures classified as repeatable.
96
+
97
+ -----
98
+
99
+ <a id="understand-the-implementation"></a>
100
+ ## Understand the implementation
101
+
102
+ <details>
103
+ <summary>Implementation internals — click to expand</summary>
104
+
105
+ The viewer page is the Owner's own Web shell. The Liveshare server fetches the Owner's rendered index document through an `asset` read and places the carrier script from [viewer-carrier.ts](../liveshare-protocol/src/viewer-carrier.ts) first in it. Shell assets, client plugin bundles, and the three exact top-level files the index references against the document base — `manifest.webmanifest`, `favicon.svg`, and `favicon-dark.svg` — arrive through the same read, from the Owner's frontend reader and client module registry, so the page runs every Owner plugin at the Owner's version. Source maps are refused on every asset path, and that check is the authoritative one: an `asset` read forwarded through the Liveshare server arrives here unread, so the Liveshare server's own same-named rule covers its page-resource route only. The served document also drops its manifest link, which a browser would otherwise fetch without credentials. The carrier converts Connection calls and Gateway streams into `POST {base}/__share/rpc` requests, and forwards the read-only page-origin owner routes the Liveshare server cannot answer — `/open-in-app/apps` and `/api/present.host` — through that channel instead of leaving them to the Liveshare server's tail. The Liveshare server supplies the authenticated actor outside the opaque body. The Owner's read handler dispatches each reader's calls and streams into its own Connection and Gateway as one Peer per share and reader, whose `share-viewer` principal carries the share and the server-asserted identity. The Gateway decides that Peer's calls, and the Session notifications it forwards to it, through [`dsh-api-remote-access`](../../api/remote-access/README.md) from the `share-viewer` policy this package registers: a served reader holds read roles on the shared Session, its own forks, and the Workspaces holding them, fork and write roles whose conditions apply the share's tier and its own fork switch, and nothing else; attachment upload rides the read roles. The read handler keeps the share-lifecycle refusals, Liveshare server authority refresh, cold Session preparation, and fork orchestration, reads the policy's write rules before it prepares or dispatches a prompt or stop, and disposes a reader's Peer when its grant ends, the share stops or expires, or a re-check refuses it. Preparing a share's source Session also starts when a reader asks for its page or opens a stream, so a host that restarted before that Session's agent was live still gains the actions it drives with; a share whose Session cannot be confined states that instead of the read-only fact. This package's browser face mounts the viewer surfaces when the carrier's `__DSH_LIVESHARE_VIEWER__` global is present and the Share action otherwise. The served index assigns the reader's own access snapshot to the global that every index sets to the owner baseline, and the page then follows the reader's `remoteAccess/follow` snapshot; CSS hiding is not authorization.
106
+
107
+ Fork requests bind the actor, share, source, operation identity, and completed prefix. A durable reservation and the recorded child let a retry of a recorded operation replay the same child; the owner's `SessionForkRequest` mints the child id, so a request interrupted after the child was created but before it was recorded forks again. Registry mutations are asynchronous and resolve only after locked read-modify-write and atomic publication; callers must await reservation, recording, and revocation. Cold activation uses the Host’s ordinary Agent lookup, with serial `agent/created` enforcement before queued input can run, not a Remote `session/prepare` endpoint. The child scope retains one fail-closed guard; reconnection replaces its enforcement installation, and releasing an old installation cannot remove its successor.
108
+
109
+ A reader's prompt reaches the Session as a `user-rpc` message whose `source.principal` names that reader, and the pre-step re-checks every entering message's viewer against the Relay's admission before it reaches the model. Execution is confined per turn: while any share over the Session is active, a turn whose admitted input names a share viewer runs the Host's confined replacements for the read and search surface (`read`, `read_image`, `glob`, `grep`, `present`), for the tools with no local file effect (`todo_write`, `skill`, `web_search`, `web_fetch`), and, when some share may write, its bounded `edit` and `write`. Every other tool is refused to that turn, and every path a permitted call declares is resolved and checked against the granted root. An owner-started turn keeps the Session's native tools until the last share stops or expires. The turn decision is Host memory only, recomputed for each turn and never appended to the Session log. Each operation re-reads the shares' current Relay grants and checks the resolved target against the granted root. A reader's stop is dispatched through the Gateway like a prompt: the `share-viewer` policy admits it while the share's tier is established and the Session is confined, and the owner's own Session controller stops whatever turn the Session is running, including one the owner started. Reads and writes preserve the normal write-observation policy. See the [LiveShare subsystem](../../../docs/subsystems/liveshare.md) for the execution rules.
110
+
111
+ | Owner | Source |
112
+ |---|---|
113
+ | Login, credential storage, and gateway lifetime | [share-host.ts](src/host/share-host.ts) |
114
+ | Share-management operations and their `liveshareShares` Remote | [share-operations.ts](src/host/share-operations.ts), [share-remote.ts](src/host/share-remote.ts) |
115
+ | Viewer request dispatch, reader Peers, and the access policy | [viewer-read.ts](src/host/viewer-read.ts), [viewer-principal.ts](src/host/viewer-principal.ts), [viewer-access-policy.ts](src/host/viewer-access-policy.ts), [viewer-declarations.ts](src/host/viewer-declarations.ts), [source-access.ts](src/host/source-access.ts) |
116
+ | Fork records and runtime enforcement | [fork-registry.ts](src/host/fork-registry.ts), [fork-enforcement.ts](src/host/fork-enforcement.ts), [shared-tools.ts](src/host/shared-tools.ts) |
117
+ | Page carrier, viewer surfaces, and served-page reads | [viewer-carrier.ts](../liveshare-protocol/src/viewer-carrier.ts), [client/index.ts](src/client/index.ts), [viewer-session-plugin.ts](src/client/viewer-session-plugin.ts), [viewer-assets.ts](src/host/viewer-assets.ts) |
118
+ | Shared identity and frame definitions | [protocol package](../liveshare-protocol/README.md) |
119
+ | Registration and socket-scoped request correlation | [registration-url.ts](src/owner-gateway/registration-url.ts), [pending-rpc.ts](src/owner-gateway/pending-rpc.ts) |
120
+
121
+ Registration declares the wire version and optional capabilities. A declared mismatch receives HTTP 426; there is no version translation. IPC requests use unique tokens bound to their original socket and worker generation; responses and cancellation match token, share id, and Liveshare server id. Unsupported requests receive a scoped refusal when they can be correlated.
122
+
123
+ The optional `share-access-state` frame and authenticated request-list response supply complete, versioned Liveshare server authority. The Host retains the Owner's selected mode separately and uses the narrower mode for execution. All complete request-list reads, including management reads, apply the same version and generation checks; missing authority invalidates admission. A request that first discovers revocation receives `403 viewer-not-authorized`, while other revoked work is cancelled. The `share-viewer` policy and `agent/pre-step` bind the receiving Session to the share's source or that actor's recorded child. Queued external input refreshes authority even after restart; a deleted share cannot authorize recovered Inbox input. See the [shared protocol reference](../liveshare-protocol/README.md) for snapshot fields.
124
+
125
+ Viewer streams belong to their opening actor. Notifications and control projections expose only the source and that actor’s children; interactive approvals and questions remain on the Host. `forkLineage` is a LiveShare-owned UI projection, not a grant. The per-Session grants of the reader's access snapshot decide whether the page offers prompt and stop, independently of display lineage.
126
+
127
+ The external Liveshare server's service, stores, OAuth provider, and deployment remain separately owned. Owner-only tests live here; tests that start both complete products remain with the upper Liveshare server integration.
128
+
129
+ </details>
130
+
131
+ -----
132
+
133
+ <a id="further-exploration"></a>
134
+ ## Further Exploration
135
+
136
+ - [LiveShare subsystem](../../../docs/subsystems/liveshare.md)
137
+ - [Owner decision record](../../../.agents/notes/implemented/feature/2026-09-13-liveshare-protocol-and-owner-surfaces.md)
138
+ - [Web bundle](../../bundle/web-app/README.md)
139
+
140
+ -----
141
+
142
+ <a id="model-experience"></a>
143
+ ## Model Experience
144
+
145
+ Indirectly, through the host driving viewer prompts into the shared Session, so a model request follows the viewer input that host forwards as that Session's ordinary user input.
146
+
147
+ #### KV Cache effect
148
+
149
+ Viewer prompts enter the shared Session as ordinary user input, so they extend the same cached prompt prefix the owner's own input extends; the bundle adds no request field, tool, or system-prompt text of its own.
150
+
151
+ ## Known Limitations and Deferred Work
152
+
153
+ <a id="known-limitations-and-deferred-work"></a>
154
+
155
+ - Owner, viewer, and the Liveshare server require matching versions; cross-version compatibility is not established.
156
+ - The owner cannot refuse attachment uploads for an admitted viewer: a viewer that may read a Session may also attach files to it, independent of the tier and of the fork switch. This is an accepted risk.
157
+ - Viewer file operations are confined by a path check against the granted root, so a local process that swaps a directory inside the grant for a symlink between the check and the open can redirect them; directory-handle anchoring is deferred to a separate change. Viewer tools are limited to the confined read and search surface (`read`, `read_image`, `glob`, `grep`, `present`), the tools with no local file effect (`todo_write`, `skill`, `web_search`, `web_fetch`), and, at the write tier, bounded `edit` and `write`; `bash`, `pwsh`, `cua`, `plugin_manager`, the `schedule_*` tools, and every tool that surface does not name are not granted.
158
+ - The viewer has no separate workspace-file editing interface and no management authority.
159
+ - Endpoint capabilities are temporary and trusted without review: the registry checks only that a name is a `<namespace>/<method>` Remote endpoint, so a selected endpoint becomes callable by every authenticated share viewer on the resources the policy binds, and the endpoint's own confinement is the only limit on what a viewer reaches through it. A selected writable endpoint is destructive inside a share that already allows writes. Capabilities cover the `global`, `session`, and `workspace` resource kinds only, and an `event:` name is rejected.
160
+ - The owner picks one exposure tier per share in the share panel — unavailable, read-only, or writable — which starts unavailable and applies to every plugin that declares endpoints; the manager lists what each share already exposes. The tier expands at save time into the endpoints the plugins declare then, so the stored record stays an explicit endpoint list and an endpoint a plugin adds later stays closed until the owner saves again. A tier the live offer cannot fill is not selectable: with no writable endpoint declared anywhere, writable is stated as unavailable instead of degrading to read-only. One save writes the share's tier, fork switch, and endpoints in that order; a refusal of the endpoint write leaves the permission write stored, states the failure, and keeps the owner's picks for a retry. The selection is share-wide: every admitted viewer of that share may call a selected endpoint on the resources the policy binds, and per-viewer endpoint levels are not established. A selection the offering plugin no longer declares exposes nothing, but stays in the stored record until the owner saves again; a record that is no whole tier reads as unavailable and says that saving replaces it.
161
+ - A Workspace that stops holding a Session the reader can see leaves the reader's Workspace feed only at that Workspace's or the Workspace order's next change ([remote access limitation](../../api/remote-access/README.md#known-limitations-and-deferred-work)).
162
+ - Registry file locking prevents concurrent file writes from dropping durable entries, but each registry reads its own in-memory snapshot. Owner share-state updates and gateway ownership are not coordinated across Hosts; use one active Owner Host per home. This is not a multi-host authorization service.
163
+ - No runtime invariant companion is published: authorization is checked in the operations themselves, and this package has no separate invariant observer.
164
+ - A worker restart re-registers retained shares but does not replay viewer streams; viewers recover through their transport. Registration refusals 401, 403, 410, and 426 stop that share’s reconnect loop until something registers it again: republishing or changing its tier does, and so does storing a credential for that Liveshare server, which registers the shares that address left offline. A restore presents the credential this host holds for the share’s Liveshare server whatever this host’s own expiry read says, because the Liveshare server renews the login behind it and only the Liveshare server decides whether that credential still authorizes; a new share is still refused on that same local read.
165
+ - The access mode grants `view` admission, while effective fork/write authority comes from the share mode. Per-viewer execution levels and immediate cross-instance stop/cancellation completion are not established; cancellation cannot undo committed writes. A write-to-readonly snapshot denies later writes but does not promise immediate cancellation of a running child turn.
166
+ - Final assembled snapshot verification remains required before the feature is ready for release.
167
+ - On the viewer page, client reads that bypass the Connection carrier (deliverable reads, open-in-app, PDF assets, HMR events) reach the Liveshare server rather than the Owner; the index document's root-absolute manifest and icon links do not resolve under a Liveshare server prefix.
168
+
169
+ <a id="dev-note"></a>
170
+ ### Dev Note
171
+
172
+ <details>
173
+ <summary>Working context for maintainers — click to expand</summary>
174
+
175
+ None.
176
+
177
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,177 @@
1
+ ---
2
+ description: "通过独立的 Liveshare 服务端分享 DSH 会话,提供已验证的所有者登录、查看者控制和受限执行。"
3
+ kind: "package-bundle"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-experimental-liveshare
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 通过常规 DSH 浏览器界面向同事分享会话。所有者选择查看或受限写入,并单独决定是否允许分叉会话;所有者主机执行这些选择。附件上传属于分享本身:查看者可见的会话都可以接收文件。这个可选组合包增加所有者登录、分享控件、出站网关,以及所有者自身 Web 页面中的查看页界面。默认 Web profile 不包含它。单独部署的 Liveshare 服务端验证查看者并传输请求;其 OAuth、数据库、路由和部署实现不属于本包。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [延伸阅读](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与后续工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ ### Profile 与构建要求
29
+
30
+ manifest(元数据清单)声明可安装的 `dsh.bundle` 层和浏览器端入口。该组合包在被 profile 启用前保持隐藏,见[默认可见性与启用](#default-visibility-and-enabling);受支持的应用仍通过 `dsh --profile` 启动。
31
+
32
+ Owner、查看页和 Liveshare 服务端必须使用匹配的协议版本。构建包含 Owner 网关进程;查看页没有单独的构建。包版本、导出和发布内容由 [package.json](package.json) 声明。
33
+
34
+ <a id="default-visibility-and-enabling"></a>
35
+
36
+ ### 默认可见性与启用
37
+
38
+ dsh 安装会把本包作为运行时依赖一起安装,但没有任何随发行版交付的 profile 选中它的组合包,因此它的插件一条都不会运行,插件管理器既不会在**内置插件配置**里列出它,也不会在**实验性插件**(含**更多**页)里列出。组合包 ID 仍可按 ID 打开,因此按 ID 打开组合包的页面仍能到达它的详情。
39
+
40
+ 要启用它,把它的两条条目插入当前 profile 的 patch 文件 `$DSH_HOME/profiles/<profile>/cordis.patch.yml`。该文件是一个 YAML 序列的 Loader patch;这两条就是组合包自己的 [patch](cordis.patch.yml) 插入的条目,其中允许使用 `!!js` 表达式:
41
+
42
+ ```yaml
43
+ - insert:
44
+ - id: liveshare-viewer-declarations
45
+ name: '@deepseek-ai/dsh-experimental-liveshare/viewer-declarations'
46
+ - id: liveshare-share-host
47
+ name: '@deepseek-ai/dsh-experimental-liveshare'
48
+ config:
49
+ ownerIdentityProvider: <provider-id>
50
+ ownerIdentityTenant: <tenant-id>
51
+ ownerIdentitySubject: <subject-id>
52
+ allowedSubjectIds: []
53
+ ownerStateFile: /path/to/owner-shares.json
54
+ ```
55
+
56
+ 把每个 `<...>` 占位符换成部署自己的值:身份 provider、tenant 与 subject 是该部署的 Liveshare 服务端配置使用的值,示例中的都是占位符而非真实身份。环境层同样可用:在 `$DSH_HOME/.env` 里设置 `LIVESHARE_*`,并保留组合包自己的 `!!js process.env.…` 表达式。Liveshare 服务端地址不属于这里的配置:首次使用时由所有者在分享弹窗里填写,见下文的所有者配置。
57
+
58
+ profile 也可以改为选中组合包:在 `$DSH_HOME/profiles/<profile>/package.json` 的 `dsh.profile.bundles` 里加入 `"@deepseek-ai/dsh-experimental-liveshare"`;卡片可见后,卡片上的开关执行的就是这次写入。
59
+
60
+ 两条路线都只在重新组合时生效。profile 的 HMR 行开启时,改动在 profile 被重新读取后应用;没有 HMR 时,等下一次 `dsh` 启动才应用。只要组合包声明的行有一条在运行,它的卡片就会出现在**实验性插件**下,也出现在**更多**页。删除这些条目、把它们置为 disabled,或把组合包从 `dsh.profile.bundles` 移除,都会停止这些行,卡片也随之从两个列表消失。
61
+
62
+ ### 所有者配置
63
+
64
+ | 变量 | 默认值 | 含义 |
65
+ |---|---|---|
66
+ | `LIVESHARE_OWNER_PROVIDER_ID` / `LIVESHARE_OWNER_TENANT_ID` / `LIVESHARE_OWNER_SUBJECT_ID` | 未设置 | 用于旧主体匹配的所有者身份;不替代 Liveshare 服务端签发的凭据 |
67
+ | `LIVESHARE_OWNER_DISPLAY_NAME` | 未设置 | 配置身份的显示标签 |
68
+ | `LIVESHARE_ALLOWED_SUBJECTS` | 空 | 分享访问规则之外的部署级主体允许列表 |
69
+ | `LIVESHARE_OWNER_STATE_FILE` | `$DSH_HOME/liveshare/owner-shares.json` | 保存分享元数据、已保存的 Liveshare 服务端地址、实例身份和管理 token |
70
+ | `LIVESHARE_OWNER_CREDENTIAL_FILE` | 状态文件旁的 `owner-credential.json` | 保存绑定至签发它的 Liveshare 服务端的凭据 |
71
+
72
+ Liveshare 服务端地址在首次使用时设置:在分享弹窗的地址输入框里填写并保存。保存的地址持久化在 owner state 文件中,之后创建的分享都发布到该地址;已经等待注册的发布在注册信息、持久记录和返回链接中保留起始服务端地址,已有分享保留记录中的 Liveshare 服务端地址。经分享管理 Remote 清除保存的地址后,这个宿主就没有地址可用——此时创建分享会明确要求先保存地址,而不是回退到某个部署默认值。读者看到的分享者名称来自 Relay 验证凭据上的显示名,本地配置不能覆盖它;凭据只带主体时显示主体标签。提供方应用密钥属于 Liveshare 服务端,不属于这里的配置。
73
+
74
+ ### 登录与管理分享
75
+
76
+ 分享对话框在 Owner 上创建短期授权事务。弹窗只向预期窗口、来源和事务返回一次性代码。Owner 使用私有校验值兑换代码并保存绑定该 Liveshare 服务端的凭据;正常浏览器登录流程不会把该凭据存入浏览器存储或 URL。过期、取消、重放或不匹配的事务均拒绝。代码兑换期间更改 Liveshare 服务端,包括切换后再切回,会在保存凭据前拒绝登录。存储提交后,探测和分享认领保留该操作的 Liveshare 服务端与凭据身份。未验证身份时禁止发布,Liveshare 服务端不支持身份绑定时也一样。
77
+
78
+ 分享管理本身走 `liveshareShares` Typert Remote:分享列表、创建、停止、档位与分叉开关变更、端点选择、Liveshare 服务端状态、所有者身份、Liveshare 服务端保存的审计以及两项服务端检查都是挂载命名空间上的方法。登录往返与凭据保存是所有者页面仍然调用的仅有的同源 HTTP 路由。
79
+
80
+ 所有者通过组织链接接纳查看者;部署的 `LIVESHARE_ALLOWED_SUBJECTS` 作用于每份分享,把组织链接分享的准入收窄到它列出的主体。已存储的 `selected` 或 `approval` 分享保留原有 ACL 与访问摘要,弹窗不再创建这类分享;`liveshareShares.create` 的 `accessMode` 与 `allowedSubjectIds` 只保留给这些记录与直接 Remote 调用方。管理分享页面列出保存的链接、档位、可用状态及 Liveshare 服务端的审计响应,并可修改分享档位、开关「允许分叉会话」以及停止分享。失败操作显示错误;界面重新读取 Owner 响应,不把点击本身当作成功。分享弹窗也能修改已有分享:档位、分叉开关与插件端点在弹窗里选择,由滚动区之外的一次保存一并提交,并在保存后回读 Owner 的记录;未保存的选择会明确标出,关闭前先提示它们会被丢弃。
81
+
82
+ ### 会话档位
83
+
84
+ | 档位 | 查看者操作 | 执行限制 |
85
+ |---|---|---|
86
+ | `view` | 向源会话发送提示词,并停止其正在运行的轮次 | 挂在会话上的只读沙箱,只约束分享查看者发起的轮次:该轮次只能使用读取与检索面(`read`、`read_image`、`glob`、`grep`、`present`)、无本地文件副作用的工具(`todo_write`、`skill`、`web_search`、`web_fetch`);每次调用声明的每个路径都按获授工作区检查 |
87
+ | `write` | 还可修改该工作区内的文件 | 有上限的可写沙箱:读取与检索面加上有界 `edit` 与 `write`,同样作用于查看者发起的轮次 |
88
+
89
+ 两档都会驱动共享会话,区别在于能否写入。沙箱挂在会话上,只要该会话还有分享活跃就会安装,但它只约束分享查看者发起的轮次:所有者自己发起的轮次保留会话原生工具,包括 `bash` 与不受限的文件工具;所有者在查看者轮次中追加的输入仍留在该受限轮次内。该判定只存在于 Host 内存、按轮次重算,不写入会话日志;所有分享停止或全部过期后执行层被释放,会话恢复自己的工具。分叉会话不是一个档位:分享自带独立开关,与档位无关;被允许的查看者可从已完成的源前缀分叉出会话,分叉会话运行在只读上限下;分享处于 `write` 档时改为受约束的写上限。
90
+
91
+ 附件上传既不是档位,也不属于分叉开关:它跟随可见性。无论分享处于哪个档位、是否允许分叉,已准入的查看者都可以向共享会话以及自己通过本分享分叉出的会话上传附件。所有者没有单独的附件开关。
92
+
93
+ 任何档位都不授予会话配置、重命名、归档、工作区管理或所有者全局设置与凭据权限;附件上传跟随可见性,不是档位授予。Owner Gateway 以 Remote 失败 `remote-access/denied`(reason 为 `liveshare/read-only`)拒绝查看者的 `settings/describe`、`credentials/describe`、`agentPresets/list`、`agentPresets/read` 以及分享未授予的其它端点;配置值、凭据引用元信息及 preset 插件配置不属于查看者数据。查看者保留本地展示默认值。这些管理决定与向会话发送消息的权限保持独立。**临时机制**:拥有本页所需端点的插件通过 [`viewerDeclarationsOf(ctx)`](src/host/viewer-declarations.ts) 提供能力——不改动任何状态的端点用 `registerViewerReadable(actions)`,会写入的端点用 `registerViewerWritable(actions)`——直到 Typert 元数据中的端点级访问声明取代这一接口。**提供能力本身不授予任何权限**:owner 按分享逐个开放某插件的端点,只有该插件的当前能力与这份分享自己的选择都指明同一端点、且资源在读者可见范围内时,该分享才暴露它。可写的选择沿用分享自身的写入规则,因此**只在会话本身已允许写入、且源执行已 confine 的分享中生效,任何能力与选择都不会把只读分享变成可写**;两种选择都把这个 policy 已经判定的 action——`session/prompt`、`session/cancel`、`session/fork` 及其 role 列出的其余 action——留给档位自己的结论。所有者自己的页面以所有者访问基线打开,界面保持不变。
94
+
95
+ Owner Gateway 按调用指明的 Session 与工作区判定每次调用,并拒绝目标缺失或无效的调用。查看页的会话列表和工作区成员裁剪为源会话及该查看者自己的分叉会话。页面从读者的访问快照读取每个会话自己的答案,因此切换选择不会把前一会话的权限带过去。未知或无法读取的模式默认拒绝。Owner 为源会话与分叉会话调用重新检查准入;撤权会取消受影响的调用和流,分享准入收窄时还会停止共享会话正在运行的轮次。页面区分认证、访问、过期、停止分享、所有者离线和传输失败,仅重试明确分类为可重试的失败。
96
+
97
+ -----
98
+
99
+ <a id="understand-the-implementation"></a>
100
+ ## 理解实现
101
+
102
+ <details>
103
+ <summary>实现内部机制 — 点击展开</summary>
104
+
105
+ 查看页就是 Owner 自己的 Web shell。Liveshare 服务端通过 `asset` 读取取得 Owner 渲染好的 index 文档,并把 [viewer-carrier.ts](../liveshare-protocol/src/viewer-carrier.ts) 生成的载体脚本放在文档最前面。Shell 资源、客户端插件 bundle,以及 index 相对文档基准引用的三个确切顶层文件(`manifest.webmanifest`、`favicon.svg`、`favicon-dark.svg`)都经同一读取取得,来自 Owner 的前端读取服务和客户端模块注册表,因此页面以 Owner 的版本运行 Owner 的全部插件。任何 asset 路径上的 source map 都被拒绝,且该判定是权威的:经 Liveshare 服务端转发的 `asset` 读取会原样到达这里,因此 Liveshare 服务端侧的同名规则只覆盖它自己的页面资源路由;被服务的文档还会去掉自己的 manifest 链接,否则浏览器会不带凭据地抓取它。载体把 Connection 调用和 Gateway 流转换为 `POST {base}/__share/rpc` 请求,并把 Liveshare 服务端无法作答的只读页面源 Owner 路由(`/open-in-app/apps` 与 `/api/present.host`)也经该通道转发,而不是留给 Liveshare 服务端尾部。Liveshare 服务端在不透明请求体之外提供已认证主体。Owner 读取处理器把每位读者的调用和流以「每个分享、每位读者一个 Peer」分派进自己的 Connection 与 Gateway,该 Peer 的 `share-viewer` principal 携带分享与服务端断言的身份。Gateway 依据本包注册的 `share-viewer` 策略,经 [`dsh-api-remote-access`](../../api/remote-access/README.zh.md) 判定该 Peer 的调用以及转发给它的会话通知:受服务的读者持有共享会话、其自己的 fork 及包含它们的工作区上的读取角色,持有条件按分享档位与其自身 fork 开关判定的 fork 与写入角色,此外别无授权;附件上传随读取角色生效。读取处理器保留分享生命周期拒绝、Liveshare 服务端授权刷新、冷会话准备和 fork 编排,在准备或分派提示词与停止之前读取策略的写入规则,并在授权结束、分享停止或过期、或复检拒绝时释放读者的 Peer。读者索取页面或打开流时也会开始准备该分享的源会话,因此宿主重启时该会话的 agent 尚未存活也能补上驱动所需的层;无法被 confine 的分享会如实说明该状态,而不再声明只读。存在载体写入的 `__DSH_LIVESHARE_VIEWER__` 全局变量时,本包的浏览器端挂载查看页界面,否则挂载分享操作。读者收到的 index 把读者自己的访问快照赋给每个 index 设为所有者基线的同一全局变量,页面随后跟随读者的 `remoteAccess/follow` 快照;CSS 隐藏不构成授权。
106
+
107
+ Fork 请求绑定主体、分享、源会话、操作身份和已完成前缀。持久化预留记录与已记录的分叉会话使重试已记录的操作时重放同一分叉会话;owner 的 `SessionForkRequest` 自己生成分叉会话 id,因此在分叉会话已创建、但尚未记录时中断的请求会再次 fork。注册表变更为异步操作,仅在加锁的读取、修改与原子发布后完成;调用方必须等待预留、记录与撤销。冷态激活使用 Host 常规 Agent lookup,由串行 `agent/created` 在排队输入运行前安装限制,不使用 Remote `session/prepare` 端点。分叉会话作用域保留单个默认拒绝的 guard;重连替换其限制安装,释放旧安装不会移除继任。
108
+
109
+ 读者的提示词以 `user-rpc` 消息进入会话,其 `source.principal` 指明该读者;pre-step 在每条进入消息到达模型前,按 Relay 的准入重新检查其查看者。执行按轮次受限:只要有分享处于活跃状态,已准入输入中带有 share-viewer principal 的轮次就运行 Host 挂载的读取与检索面(`read`、`read_image`、`glob`、`grep`、`present`)受限替代、无本地文件副作用工具(`todo_write`、`skill`、`web_search`、`web_fetch`)的替代,以及(当某个分享允许写入时)有界 `edit` 与 `write`。其余所有工具对该轮次一律拒绝,且每次允许的调用声明的每个路径都会解析并按获授根目录检查。所有者自己发起的轮次保留会话原生工具,直到最后一个分享停止或过期。该轮次判定只存在于 Host 内存,按轮次重算,不写入会话日志。每次操作都会重新读取分享当前的 Relay 授权,并按获授根目录检查解析后的目标。读者的停止与提示词一样经 Gateway 分派:`share-viewer` 策略在分享档位确立且会话受沙箱限制时放行,随后由所有者自己的会话控制器停止该会话当前运行的轮次,包括所有者发起的轮次。读写保留正常写入观察策略。执行规则见 [LiveShare 子系统](../../../docs/subsystems/liveshare.zh.md)。
110
+
111
+ | 职责 | 源码 |
112
+ |---|---|
113
+ | 登录、凭据存储及网关生命周期 | [share-host.ts](src/host/share-host.ts) |
114
+ | 分享管理操作及其 `liveshareShares` Remote | [share-operations.ts](src/host/share-operations.ts), [share-remote.ts](src/host/share-remote.ts) |
115
+ | 查看请求分派、读者 Peer 与访问策略 | [viewer-read.ts](src/host/viewer-read.ts)、[viewer-principal.ts](src/host/viewer-principal.ts)、[viewer-access-policy.ts](src/host/viewer-access-policy.ts)、[viewer-declarations.ts](src/host/viewer-declarations.ts)、[source-access.ts](src/host/source-access.ts) |
116
+ | Fork 记录与运行时限制 | [fork-registry.ts](src/host/fork-registry.ts)、[fork-enforcement.ts](src/host/fork-enforcement.ts)、[shared-tools.ts](src/host/shared-tools.ts) |
117
+ | 页面载体、查看页界面和页面资源读取 | [viewer-carrier.ts](../liveshare-protocol/src/viewer-carrier.ts)、[client/index.ts](src/client/index.ts)、[viewer-session-plugin.ts](src/client/viewer-session-plugin.ts)、[viewer-assets.ts](src/host/viewer-assets.ts) |
118
+ | 共享身份和帧定义 | [协议包](../liveshare-protocol/README.zh.md) |
119
+ | 注册与 socket 范围请求关联 | [registration-url.ts](src/owner-gateway/registration-url.ts)、[pending-rpc.ts](src/owner-gateway/pending-rpc.ts) |
120
+
121
+ 注册声明线路版本与可选能力。声明的版本不匹配时返回 HTTP 426;不做版本转换。IPC 请求使用绑定原始 socket 和 worker generation 的唯一 token;响应与取消匹配 token、分享 ID 和 Liveshare 服务端 ID。可关联的不支持请求会收到作用域内拒绝。
122
+
123
+ 可选 `share-access-state` 帧及鉴权请求列表响应提供完整、带版本的 Liveshare 服务端授权。Host 独立保留 Owner 选择的模式,执行时使用两者中较窄的模式。所有完整请求列表读取,包括管理读取,都应用同一版本与 generation 校验;缺失授权使准入失效。首次发现撤权的请求收到 `403 viewer-not-authorized`,其它已撤权工作被取消。`share-viewer` 策略及 `agent/pre-step` 将接收 Session 绑定到分享源会话或该主体记录的分叉会话。排队外部输入在重启后仍刷新授权;已删除分享不能授权恢复的 Inbox 输入。快照字段见 [共享协议参考](../liveshare-protocol/README.zh.md)。
124
+
125
+ 查看流属于打开它的主体。通知与控制投影只暴露源会话及该主体的分叉会话;交互审批与提问留在 Host。`forkLineage` 是 LiveShare 自有 UI 投影,不是授权。读者访问快照中的逐会话 grant 独立于展示谱系,决定页面是否提供提示词与停止。
126
+
127
+ 外部 Liveshare 服务端的服务、存储、OAuth 提供方和部署保持独立归属。仅涉及 Owner 的测试位于本包;启动两端完整产品的测试留在上层 Liveshare 服务端集成中。
128
+
129
+ </details>
130
+
131
+ -----
132
+
133
+ <a id="further-exploration"></a>
134
+ ## 延伸阅读
135
+
136
+ - [LiveShare 子系统](../../../docs/subsystems/liveshare.zh.md)
137
+ - [Owner 决策记录](../../../.agents/notes/implemented/feature/2026-09-13-liveshare-protocol-and-owner-surfaces.zh.md)
138
+ - [Web 组合包](../../bundle/web-app/README.zh.md)
139
+
140
+ -----
141
+
142
+ <a id="model-experience"></a>
143
+ ## 模型体验
144
+
145
+ 间接触达:host 把 viewer 的 prompt 驱动进共享会话,模型请求因而承载该 host 以会话普通用户输入转发的 viewer 内容。
146
+
147
+ #### KV Cache 影响
148
+
149
+ viewer 的 prompt 以普通用户输入进入共享会话,因此扩展的是与所有者自身输入相同的缓存 prompt 前缀;本组合包不增加任何请求字段、工具或系统提示文本。
150
+
151
+ ## 已知限制与后续工作
152
+
153
+ <a id="known-limitations-and-deferred-work"></a>
154
+
155
+ - Owner、查看页和 Liveshare 服务端需要匹配版本;尚未建立跨版本兼容保证。
156
+ - 所有者无法拒绝已准入查看者的附件上传:能读取某条会话的查看者也可以向它附加文件,与档位和分叉开关无关。这是已接受的风险。
157
+ - 查看者的文件操作按获授根目录做路径检查,若本地进程在检查与打开之间把获授范围内的目录换成符号链接,读写可能被改指别处;目录句柄锚定留待单独变更引入。查看者工具仅限受限的读取与检索面(`read`、`read_image`、`glob`、`grep`、`present`)、无本地文件副作用的工具(`todo_write`、`skill`、`web_search`、`web_fetch`),以及 write 档下的有界 `edit` 与 `write`;不授予 `bash`、`pwsh`、`cua`、`plugin_manager`、`schedule_*` 及该工具面未列出的任何工具。
158
+ - 查看页没有独立工作区文件编辑界面,也没有管理权限。
159
+ - 端点能力是临时机制且不经审查即被信任:注册表只校验名称形如 `<namespace>/<method>` 的 Remote 端点,因此被勾选的端点会在 policy 绑定的资源上被任意已认证分享读者调用,端点自身的 confine 是唯一限制。被勾选的可写端点在已允许写入的分享中具有破坏性。能力只覆盖 `global`、`session`、`workspace` 三种资源种类,`event:` 名称会被拒绝。
160
+ - owner 在分享面板里为每份分享选一个开放档位——不可用 / 可读 / 可写——默认不可用,统一作用于所有声明了端点的插件;分享管理里能看到每份分享已开放的端点。档位在保存时展开成插件当时声明的端点,因此记录始终是显式端点列表,插件之后新增的端点不会自动开放,直到 owner 再次保存。当前能力填不满的档位不可选:没有任何插件声明可写端点时,「可写」按不可用呈现,而不是降级成可读。一次保存按顺序写这三项(档位、分叉开关、端点);靠后的端点写入被拒时,前面的权限写入已经生效,界面报错并保留 owner 的选择以便重试。选择是分享级的:该分享的每位已准入查看者都可以在 policy 绑定的资源上调用被选中的端点,尚未建立逐查看者端点级别。插件不再声明的端点即使留在记录里也不暴露;不是整档的记录按不可用显示并说明重新保存会覆盖它。
161
+ - 工作区不再包含读者可见的会话后,要到该工作区或工作区顺序下次变化时才离开读者的工作区订阅([remote access 限制](../../api/remote-access/README.zh.md#known-limitations-and-deferred-work))。
162
+ - 注册表文件锁防止并发文件写入丢失持久条目,但每个注册表读取自身内存快照。多个 Host 之间不协调 Owner 分享状态更新与网关归属;每个 home 只使用一个活动 Owner Host。这不是多宿主授权服务。
163
+ - 不发布运行时 invariant 配套模块:授权在操作本身检查,本包没有单独的 invariant 观察器。
164
+ - Worker 重启会重新注册保留分享,但不重放查看流;查看者通过传输层恢复。注册被 401、403、410 或 426 拒绝时,该分享的重连就此停止,直到有别的操作把它重新注册:重新发布或改档位都会重新注册,为该 Liveshare 服务端存入凭据也会,它会把该地址下未在线的分享重新注册。恢复分享时出示的是本机为该分享所属 Liveshare 服务端保存的凭据,与本机自己对凭据过期的判断无关,因为 Liveshare 服务端会在背后为登录续期,凭据是否仍然授权只由 Liveshare 服务端决定;新建分享时仍按本机这一判断拒绝。
165
+ - 访问档位授予 `view` 准入,有效 fork/write 权限来自分享模式。尚未建立逐查看者执行级别或立即跨实例停止/取消完成保证;取消不能撤回已提交写入。写入降为只读的快照会拒绝后续写入,但不承诺立即取消正在运行的分叉会话轮次。
166
+ - 功能达到发布条件前,仍须完成最终装配快照验证。
167
+ - 在查看页上,绕过 Connection 载体的客户端读取(交付物读取、open-in-app、PDF 资源、HMR 事件)到达的是 Liveshare 服务端而不是 Owner;index 文档中以根路径开头的 manifest 和图标链接在 Liveshare 服务端前缀下无法解析。
168
+
169
+ <a id="dev-note"></a>
170
+ ### 开发备注
171
+
172
+ <details>
173
+ <summary>维护者工作上下文 — 点击展开</summary>
174
+
175
+ 无。
176
+
177
+ </details>
@@ -0,0 +1,17 @@
1
+ # Opt-in Liveshare owner bundle: mount the same-origin share APIs and their
2
+ # browser Session-header action. Shipped profiles do not include this layer.
3
+ - insert:
4
+ # The declaration surface loads first and needs nothing: the share host below waits for a Relay
5
+ # connection, so a surface it provided would appear after the plugins that offer endpoints did.
6
+ - id: liveshare-viewer-declarations
7
+ name: '@deepseek-ai/dsh-experimental-liveshare/viewer-declarations'
8
+ - id: liveshare-share-host
9
+ name: '@deepseek-ai/dsh-experimental-liveshare'
10
+ config:
11
+ ownerIdentityProvider: !!js process.env.LIVESHARE_OWNER_PROVIDER_ID
12
+ ownerIdentityTenant: !!js process.env.LIVESHARE_OWNER_TENANT_ID
13
+ ownerIdentitySubject: !!js process.env.LIVESHARE_OWNER_SUBJECT_ID
14
+ ownerIdentityDisplayName: !!js process.env.LIVESHARE_OWNER_DISPLAY_NAME
15
+ allowedSubjectIds: !!js (process.env.LIVESHARE_ALLOWED_SUBJECTS ?? '').split(',').map((value) => value.trim()).filter((value) => value !== '')
16
+ ownerStateFile: !!js process.env.LIVESHARE_OWNER_STATE_FILE
17
+ ownerCredentialFile: !!js process.env.LIVESHARE_OWNER_CREDENTIAL_FILE
package/icon.svg ADDED
@@ -0,0 +1,12 @@
1
+ <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect x="5.5" y="8.5" width="25" height="17" rx="2.6" fill="url(#a)" fill-opacity="0.85" />
3
+ <path d="M9.5 22.5V12.5H17.5V22.5H9.5Z" fill="#FFFFFF" fill-opacity="0.9" />
4
+ <path d="M21 15.2L25.5 12.6V23.4L21 20.8V15.2Z" fill="#45D9E7" />
5
+ <circle cx="28.5" cy="9.5" r="3.2" fill="#45D9E7" />
6
+ <defs>
7
+ <linearGradient id="a" x1="18" y1="8.5" x2="18" y2="25.5" gradientUnits="userSpaceOnUse">
8
+ <stop stop-color="#7CB7FF" />
9
+ <stop offset="1" stop-color="#145AF3" />
10
+ </linearGradient>
11
+ </defs>
12
+ </svg>