@yadsh/dsh-qa-surface 0.1.1 → 0.3.0

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 (192) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/README.md +239 -16
  3. package/SPEC-dsh-qa-surface.md +600 -96
  4. package/capability-policy.json +5 -0
  5. package/compatibility.json +18 -4
  6. package/cordis.patch.yml +11 -0
  7. package/docs/ARCHITECTURE.md +50 -8
  8. package/docs/COMPATIBILITY.md +5 -4
  9. package/docs/CONFIGURATION.md +184 -3
  10. package/docs/SOURCES-MIGRATION.md +28 -0
  11. package/docs/SOURCES-PROVENANCE-AUDIT.md +88 -0
  12. package/docs/SPEC-dsh-qa-surface-accounts.md +219 -0
  13. package/docs/SPEC-dsh-qa-surface-sources-provenance.md +1692 -0
  14. package/lib/accounts/index.js +2 -0
  15. package/lib/accounts/index.js.map +1 -0
  16. package/lib/accounts/store.js +478 -0
  17. package/lib/accounts/store.js.map +1 -0
  18. package/lib/attestation.js +10 -0
  19. package/lib/attestation.js.map +1 -0
  20. package/lib/cli.js +143 -0
  21. package/lib/cli.js.map +1 -0
  22. package/lib/client/QaAccountsController.js +329 -0
  23. package/lib/client/QaAccountsController.js.map +1 -0
  24. package/lib/client/QaConfigController.js +111 -4
  25. package/lib/client/QaConfigController.js.map +1 -1
  26. package/lib/client/QaSessionController.js +621 -163
  27. package/lib/client/QaSessionController.js.map +1 -1
  28. package/lib/client/QaSurface.js +303 -24
  29. package/lib/client/QaSurface.js.map +1 -1
  30. package/lib/client/QaTranscriptAdapter.js +324 -49
  31. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  32. package/lib/client/attestation.js +66 -0
  33. package/lib/client/attestation.js.map +1 -0
  34. package/lib/client/chat-index.js +86 -0
  35. package/lib/client/chat-index.js.map +1 -0
  36. package/lib/client/components/Markdown.js +124 -13
  37. package/lib/client/components/Markdown.js.map +1 -1
  38. package/lib/client/components/QaAgentsDrawer.js +40 -0
  39. package/lib/client/components/QaAgentsDrawer.js.map +1 -0
  40. package/lib/client/components/QaAuthGate.js +40 -0
  41. package/lib/client/components/QaAuthGate.js.map +1 -0
  42. package/lib/client/components/QaChangelog.js +112 -0
  43. package/lib/client/components/QaChangelog.js.map +1 -0
  44. package/lib/client/components/QaComposer.js +132 -15
  45. package/lib/client/components/QaComposer.js.map +1 -1
  46. package/lib/client/components/QaMessage.js +214 -10
  47. package/lib/client/components/QaMessage.js.map +1 -1
  48. package/lib/client/components/QaSidebar.js +167 -0
  49. package/lib/client/components/QaSidebar.js.map +1 -0
  50. package/lib/client/components/QaSourcesDrawer.js +127 -0
  51. package/lib/client/components/QaSourcesDrawer.js.map +1 -0
  52. package/lib/client/components/QaTurnRail.js +281 -0
  53. package/lib/client/components/QaTurnRail.js.map +1 -0
  54. package/lib/client/components/QaWelcomeNotice.js +125 -0
  55. package/lib/client/components/QaWelcomeNotice.js.map +1 -0
  56. package/lib/client/components/QaWidthHandle.js +142 -0
  57. package/lib/client/components/QaWidthHandle.js.map +1 -0
  58. package/lib/client/components/QaWorkGroup.js +135 -0
  59. package/lib/client/components/QaWorkGroup.js.map +1 -0
  60. package/lib/client/components/VariantSwitcher.js +29 -0
  61. package/lib/client/components/VariantSwitcher.js.map +1 -0
  62. package/lib/client/components/format.js +48 -0
  63. package/lib/client/components/format.js.map +1 -0
  64. package/lib/client/components/source-refs.js +78 -0
  65. package/lib/client/components/source-refs.js.map +1 -0
  66. package/lib/client/create-session.js +51 -0
  67. package/lib/client/create-session.js.map +1 -0
  68. package/lib/client/index.js +166 -35
  69. package/lib/client/index.js.map +1 -1
  70. package/lib/client/project-session-state.js +78 -0
  71. package/lib/client/project-session-state.js.map +1 -0
  72. package/lib/client/session-admission.js +38 -0
  73. package/lib/client/session-admission.js.map +1 -0
  74. package/lib/client/session-assets.js +52 -0
  75. package/lib/client/session-assets.js.map +1 -0
  76. package/lib/client/session-sources.js +61 -0
  77. package/lib/client/session-sources.js.map +1 -0
  78. package/lib/client/settlement.js +24 -0
  79. package/lib/client/settlement.js.map +1 -0
  80. package/lib/client/styles.js +450 -43
  81. package/lib/client/styles.js.map +1 -1
  82. package/lib/client/tool-presentation.js +102 -0
  83. package/lib/client/tool-presentation.js.map +1 -0
  84. package/lib/client/turn-sources.js +98 -0
  85. package/lib/client/turn-sources.js.map +1 -0
  86. package/lib/client/types.js +15 -0
  87. package/lib/client/types.js.map +1 -0
  88. package/lib/client/use-session-ui-state.js +68 -0
  89. package/lib/client/use-session-ui-state.js.map +1 -0
  90. package/lib/client/use-transcript-view.js +44 -0
  91. package/lib/client/use-transcript-view.js.map +1 -0
  92. package/lib/client/wait-for.js +49 -0
  93. package/lib/client/wait-for.js.map +1 -0
  94. package/lib/client.js +12551 -918
  95. package/lib/client.js.map +1 -1
  96. package/lib/config.js +226 -58
  97. package/lib/config.js.map +1 -1
  98. package/lib/entry-redirect.js +44 -0
  99. package/lib/entry-redirect.js.map +1 -0
  100. package/lib/host-route.js +25 -1
  101. package/lib/host-route.js.map +1 -1
  102. package/lib/index.js +354 -72
  103. package/lib/index.js.map +1 -1
  104. package/lib/launch-token.js +42 -0
  105. package/lib/launch-token.js.map +1 -0
  106. package/lib/lockdown-policy.js +20 -0
  107. package/lib/lockdown-policy.js.map +1 -0
  108. package/lib/provenance/collector.js +40 -0
  109. package/lib/provenance/collector.js.map +1 -0
  110. package/lib/provenance/connectors.js +147 -0
  111. package/lib/provenance/connectors.js.map +1 -0
  112. package/lib/provenance/dedupe.js +89 -0
  113. package/lib/provenance/dedupe.js.map +1 -0
  114. package/lib/provenance/extractors.js +340 -0
  115. package/lib/provenance/extractors.js.map +1 -0
  116. package/lib/provenance/file-preview.js +39 -0
  117. package/lib/provenance/file-preview.js.map +1 -0
  118. package/lib/provenance/host-store.js +387 -0
  119. package/lib/provenance/host-store.js.map +1 -0
  120. package/lib/provenance/index.js +9 -0
  121. package/lib/provenance/index.js.map +1 -0
  122. package/lib/provenance/normalize.js +128 -0
  123. package/lib/provenance/normalize.js.map +1 -0
  124. package/lib/provenance/reported.js +53 -0
  125. package/lib/provenance/reported.js.map +1 -0
  126. package/lib/provenance/types.js +2 -0
  127. package/lib/provenance/types.js.map +1 -0
  128. package/lib/resolve-config.js +283 -10
  129. package/lib/resolve-config.js.map +1 -1
  130. package/lib/secure-session.js +212 -0
  131. package/lib/secure-session.js.map +1 -0
  132. package/lib/typert.host.d.ts +3 -0
  133. package/lib/typert.host.js +784 -0
  134. package/lib/typert.remote-client.d.ts +40 -0
  135. package/lib/typert.remote-client.js +597 -0
  136. package/lib/types/accounts/index.d.ts +2 -0
  137. package/lib/types/accounts/store.d.ts +121 -0
  138. package/lib/types/attestation.d.ts +17 -0
  139. package/lib/types/cli.d.ts +9 -0
  140. package/lib/types/client/QaAccountsController.d.ts +95 -0
  141. package/lib/types/client/QaConfigController.d.ts +35 -3
  142. package/lib/types/client/QaSessionController.d.ts +146 -20
  143. package/lib/types/client/QaSurface.d.ts +11 -5
  144. package/lib/types/client/QaTranscriptAdapter.d.ts +13 -3
  145. package/lib/types/client/attestation.d.ts +25 -0
  146. package/lib/types/client/chat-index.d.ts +28 -0
  147. package/lib/types/client/components/Markdown.d.ts +17 -3
  148. package/lib/types/client/components/QaAgentsDrawer.d.ts +25 -0
  149. package/lib/types/client/components/QaAuthGate.d.ts +15 -0
  150. package/lib/types/client/components/QaChangelog.d.ts +21 -0
  151. package/lib/types/client/components/QaComposer.d.ts +12 -2
  152. package/lib/types/client/components/QaMessage.d.ts +22 -2
  153. package/lib/types/client/components/QaSidebar.d.ts +69 -0
  154. package/lib/types/client/components/QaSourcesDrawer.d.ts +20 -0
  155. package/lib/types/client/components/QaTurnRail.d.ts +59 -0
  156. package/lib/types/client/components/QaWelcomeNotice.d.ts +27 -0
  157. package/lib/types/client/components/QaWidthHandle.d.ts +36 -0
  158. package/lib/types/client/components/QaWorkGroup.d.ts +19 -0
  159. package/lib/types/client/components/VariantSwitcher.d.ts +19 -0
  160. package/lib/types/client/components/format.d.ts +9 -0
  161. package/lib/types/client/components/source-refs.d.ts +16 -0
  162. package/lib/types/client/create-session.d.ts +16 -0
  163. package/lib/types/client/index.d.ts +2 -5
  164. package/lib/types/client/project-session-state.d.ts +30 -0
  165. package/lib/types/client/session-admission.d.ts +23 -0
  166. package/lib/types/client/session-assets.d.ts +20 -0
  167. package/lib/types/client/session-sources.d.ts +28 -0
  168. package/lib/types/client/settlement.d.ts +6 -0
  169. package/lib/types/client/tool-presentation.d.ts +6 -0
  170. package/lib/types/client/turn-sources.d.ts +14 -0
  171. package/lib/types/client/types.d.ts +93 -0
  172. package/lib/types/client/use-session-ui-state.d.ts +35 -0
  173. package/lib/types/client/use-transcript-view.d.ts +20 -0
  174. package/lib/types/client/wait-for.d.ts +11 -0
  175. package/lib/types/entry-redirect.d.ts +20 -0
  176. package/lib/types/host-route.d.ts +14 -1
  177. package/lib/types/index.d.ts +67 -7
  178. package/lib/types/launch-token.d.ts +21 -0
  179. package/lib/types/lockdown-policy.d.ts +18 -0
  180. package/lib/types/provenance/collector.d.ts +26 -0
  181. package/lib/types/provenance/connectors.d.ts +11 -0
  182. package/lib/types/provenance/dedupe.d.ts +6 -0
  183. package/lib/types/provenance/extractors.d.ts +14 -0
  184. package/lib/types/provenance/file-preview.d.ts +10 -0
  185. package/lib/types/provenance/host-store.d.ts +54 -0
  186. package/lib/types/provenance/index.d.ts +10 -0
  187. package/lib/types/provenance/normalize.d.ts +13 -0
  188. package/lib/types/provenance/reported.d.ts +5 -0
  189. package/lib/types/provenance/types.d.ts +78 -0
  190. package/lib/types/secure-session.d.ts +31 -0
  191. package/lib/types/types.d.ts +304 -1
  192. package/package.json +70 -27
package/CHANGELOG.md CHANGED
@@ -1,3 +1,119 @@
1
+ ## 0.3.0 (2026-09-12)
2
+
3
+ ### 🚀 Features
4
+
5
+ - Optional QA accounts and entry routing. `accounts.enabled` mounts a ([dc2f582](https://github.com/xarleyn/dsh-plugins/commit/dc2f582))
6
+ full-frame login/registration gate (email + password, coarse audience-safe
7
+ refusals, per-store rate limiting, self-registration toggle) backed by
8
+ `$DSH_HOME/qa-accounts.json`: scrypt password hashes, a persisted HMAC secret
9
+ for stateful-expiry account tokens, and a session ownership map. Ownership is
10
+ first come, first served - attesting or bulk-claiming an unowned session binds
11
+ it to the caller (the migration path for existing per-browser chats on first
12
+ login); sessions owned by another user refuse attestation with
13
+ `session-owned-elsewhere` and stay hidden from the sidebar, admins are not
14
+ refused. The identity rides as an explicit token argument into the gated
15
+ `qaSurface` remotes (the typert carrier never exposes HTTP requests), the
16
+ policy admission checks it before any session fact is revealed
17
+ (`auth-required` reopens the gate on expiry), and the account chip with
18
+ logout lives in the sidebar footer. `entry.redirectNonLoopback` injects a
19
+ guarded head script through the `webserver/index-inject` event that continues
20
+ non-loopback hostnames into the QA route - the navigation-marker hand-off is
21
+ never redirected (no loops), `/?ui=admin` persists an operator bypass and
22
+ `/?ui=qa` clears it. Accounts are an identity layer for the QA surface, not a
23
+ harness boundary: QA users still hold the shared host launch-token cookie.
24
+
25
+ Follow-ups adopted from a review of the independent dsh-auth-gate plugin: a
26
+ proxy-side deny list for the privileged config-plane RPC methods behind the
27
+ deploy proxy's Host/Origin rewrite, a plugin-side launch-token bridge
28
+ (`entry.cookieBootstrap`) that performs the one-time host-cookie exchange on
29
+ the `/qa` route itself, a `qa-accounts` bin CLI (list/add/set-role/disable/
30
+ enable/revoke) so account administration never requires hand-editing the
31
+ JSON file, and per-account state — a `disabled` flag refusing logins with
32
+ `account-disabled` plus a `tokenVersion` burned into tokens that
33
+ `disable`/`revoke` bump, making logout and lockout server-side facts.
34
+
35
+ Admins get cross-user views over the same ownership map:
36
+ `qaSurface/accountsListOwnership` (admin-only, `admin-required` refusal
37
+ otherwise) returns every chat with its owner's resolved display name, the
38
+ admin sidebar switches to per-owner sections ordered by their freshest chat
39
+ (unclaimed chats trail under "Без владельца"), and user messages in foreign
40
+ chats carry an `author` byline naming the chat owner. Ordinary accounts and
41
+ deployments with accounts disabled keep the flat sidebar and unlabeled
42
+ messages.
43
+
44
+ - The chat-history sidebar gained a footer version button that opens an ([d9d5868](https://github.com/xarleyn/dsh-plugins/commit/d9d5868))
45
+ end-user changelog dialog: a curated per-version summary (new features and
46
+ fixes in Russian) rendered in a themed modal with Escape/backdrop close.
47
+ The bundled version and entries are pinned to package.json and the release
48
+ CHANGELOG by a unit test, so a release cannot ship a stale dialog.
49
+
50
+ - Rebuild the client on the 0.1.5 surfaces: the transcript projects from the ([458b9c2](https://github.com/xarleyn/dsh-plugins/commit/458b9c2))
51
+ ui-chat conversation view's legacy slice, chat/model pinning moves to the
52
+ wire remotes (`agentPresets.select` on the still-blank session, then
53
+ `session.selectModel`) with the attestation ordering preserved, and
54
+ history reads go through the session-v3 surface. The supported host range
55
+ moves to `>=0.1.5-rc.2 <0.2.0`, dropping 0.1.1-rc.2.
56
+
57
+ - Harden the gated QA experience and make cross-user history explicitly ([2ed2024](https://github.com/xarleyn/dsh-plugins/commit/2ed2024))
58
+ opt-in. A new `accounts.showOtherUsersChats` setting defaults to `false`, so
59
+ administrators only see their own chats unless the deployment enables the
60
+ shared ownership view. Account storage now follows external CLI updates and
61
+ uses process-scoped temporary writes, while session admission and client state
62
+ handling avoid stale async results and reset session-bound assets reliably.
63
+
64
+ The QA client now presents a dedicated test-interface disclosure, improves
65
+ chat search and owner matching, keeps row actions from disturbing result
66
+ layout, distinguishes administrator roles, and removes decorative middle-dot
67
+ separators from the sidebar, messages, and source details. Its curated 0.3.0
68
+ history entry is prepared in advance, while the current-version marker is
69
+ injected from package.json at build time so the release bump promotes it
70
+ without another source edit.
71
+
72
+ ### ❤️ Thank You
73
+
74
+ - xarleyn @xarleyn
75
+
76
+ ## 0.2.0 (2026-09-10)
77
+
78
+ - Added DSH-style symmetric transcript/composer width handles with adaptive
79
+ defaults, viewport clamping, and per-route browser persistence.
80
+ - Added Host-owned structured source provenance for parent and delegated turns,
81
+ replayable `qa/sources` snapshots, dedupe/ranking, opaque-provider reporting,
82
+ grouped source UI, and safe rendered/raw file previews.
83
+ - Updated packed Host/browser smoke coverage for browser authentication,
84
+ revisioned client batches, scoped Remote injection, and current Typert RPC
85
+ envelopes.
86
+
87
+ ### 🚀 Features
88
+
89
+ - Make the QA surface a complete end-user assistant shell: image attachments ([b9af082](https://github.com/xarleyn/dsh-plugins/commit/b9af082))
90
+ (drag & drop, paste, and a picker with removable previews; base64 prompt
91
+ parts the Host promotes to durable attachments, rendered back as clickable
92
+ thumbnails), subagent delegation presentation (launch work items with the
93
+ durable child id, settlement notices collapsed into titled expandable rows,
94
+ an agents panel listing the chat's subagents, and live read-only subagent
95
+ transcripts with a one-click return), source cards with a full-output detail
96
+ pane, clickable links and an open action, answer regeneration with
97
+ ChatGPT-style variant switching, message ratings with hover response
98
+ metadata (duration, TTFT, tokens per second), a data-usage disclaimer
99
+ plate under the composer, per-browser chat history
100
+ ordered by host updates with search and a collapsible sidebar, lazy draft
101
+ chats that create nothing until the first prompt, directory/workspace
102
+ pinning with a dedicated `workspace-unavailable` refusal, the company
103
+ interaction palette as `--dsh-qa-*` tokens, and a split of the surface into
104
+ focused drawer, switcher, and formatting modules.
105
+
106
+ ### 🩹 Fixes
107
+
108
+ - Pack the generated Typert host and remote-client entrypoints: the `files` ([0059cb4](https://github.com/xarleyn/dsh-plugins/commit/0059cb4))
109
+ allowlist only kept declarations under `lib/types/`, so the `./remote` and
110
+ `./typert` exports previously shipped without their implementation modules
111
+ and type definitions.
112
+
113
+ ### ❤️ Thank You
114
+
115
+ - xarleyn @xarleyn
116
+
1
117
  ## 0.1.1 (2026-09-06)
2
118
 
3
119
  ### 🩹 Fixes
@@ -23,3 +139,38 @@
23
139
  - Added Host settings registration and safety filtering for internal events.
24
140
  - Added a narrow Host navigation redirect for DSH releases whose static
25
141
  frontend returns 404 for direct `/qa` requests.
142
+ - Added fail-closed Host policy attestation, the `qa-read-only` permission pin,
143
+ an inherited-tool allow-list, and a monotonic execution guard.
144
+ - Disabled session reset by default and added capability-regression gates.
145
+ - Refined the QA surface with first-party-style conversation chrome, plain
146
+ assistant flow, user bubbles, copy actions and a floating two-row composer.
147
+ - Added opt-in reasoning and tool-call details grouped into a live turn work
148
+ disclosure that collapses to a `Worked for …` summary before the final answer.
149
+ - Added an optional minimal chat-history sidebar (`ui.showSessionList`) with a
150
+ per-browser localStorage chat index, attested switching and a gated
151
+ New chat control.
152
+ - Send-time policy attestation now survives a Host that idled the session's
153
+ agent out: the client re-binds the session (re-materializing the agent) and,
154
+ when the refused session is still blank, continues in a fresh attested
155
+ session instead of surfacing an error.
156
+ - Added GFM table, ordered-list and horizontal-rule rendering to the safe
157
+ Markdown output.
158
+ - Added a two-click chat delete control to the chat-history sidebar; it
159
+ removes the chat from the per-browser index only (DSH has no
160
+ session-deletion seam).
161
+ - Added attestation diagnostics: the Host folds a coarse reason code into the
162
+ refusal and the browser console prints one operator hint instead of a
163
+ duplicate stack trace.
164
+ - Fixed repeated policy attestation for tools contributed by an agent preset:
165
+ the applied restriction now retains the exact scoped allow-list instead of
166
+ accidentally reducing it to global-only tools. Removed the redundant native
167
+ presentation override so reloads cannot collide with the preset's mode.
168
+ - The browser re-reads the Host configuration when the connection is restored,
169
+ so an open page survives a Host restart with a changed deployment config.
170
+ - Added LAN serving support: a shipped `deploy/qa-lan.patch.yml` webserver
171
+ overlay, and a `qaSurface/describe` Host Remote the browser falls back to
172
+ when the loopback-pinned settings namespace is unavailable, so branding,
173
+ session pinning and lockdown UI switches keep working over the network.
174
+ - Localized the end-user chat interface into Russian, added rotating playful
175
+ thinking phrases, and moved the default quick-question chips next to the
176
+ composer on an empty chat.
package/README.md CHANGED
@@ -11,8 +11,21 @@ Session and Agent Loop.
11
11
  - stays invisible outside the configured route;
12
12
  - creates or restores one real DSH Session;
13
13
  - renders only user text, assistant-visible text and safe status messages;
14
- - supports streaming, Stop, New chat, safe Markdown and mobile layouts;
14
+ - supports streaming, Stop, optional New chat, safe Markdown, copy actions and
15
+ a responsive first-party-style conversation layout;
16
+ - optionally shows a minimal per-browser chat-history sidebar
17
+ (`ui.showSessionList`) whose switching re-runs policy attestation;
15
18
  - blocks unsupported approvals/questions instead of auto-approving them;
19
+ - pins locked sessions to `read-only` + `approval=never` before Send is enabled;
20
+ - applies a Host-side tool allow-list plus a monotonic execution guard;
21
+ - optionally gates the surface behind email + password accounts
22
+ (`accounts.enabled`) with server-side session ownership, a first-login
23
+ migration of the browser's existing chats, a `qa-accounts` management CLI
24
+ (list/add/set-role/disable/revoke), and a coarse honest boundary:
25
+ accounts identify QA users, they do not fence the harness root;
26
+ - optionally redirects non-loopback hostnames from the harness root into the
27
+ QA route (`entry.redirectNonLoopback`), keeping the operator's localhost
28
+ harness UI untouched;
16
29
  - uses the existing same-origin DSH connection and trust boundary.
17
30
 
18
31
  It does not add another HTTP server, provider proxy, permissive CORS rule, or
@@ -38,8 +51,8 @@ Some DSH patch operations replace a row's complete `config` instead of deeply
38
51
  merging it. When editing an existing row, provide every value that deployment
39
52
  needs.
40
53
 
41
- DSH `0.1.1-rc.2` serves unknown frontend paths as 404 rather than falling back
42
- to `index.html`. The Host half therefore claims only the configured QA
54
+ Tested DSH releases serve unknown frontend paths as 404 rather than falling
55
+ back to `index.html`. The Host half therefore claims only the configured QA
43
56
  navigation path and redirects it through the canonical `/` document with a
44
57
  short-lived query marker. The browser restores the requested `/qa` URL with
45
58
  `history.replaceState` before mounting the overlay. No second server, duplicate
@@ -58,14 +71,17 @@ config:
58
71
  path: /qa
59
72
  matchChildren: true
60
73
  branding:
61
- title: Internal Assistant
62
- subtitle: Answers about the internal platform
63
- welcomeMessage: How can I help?
64
- placeholder: Ask a question...
74
+ title: Внутренний помощник
75
+ subtitle: Отвечает на вопросы о внутренней платформе
76
+ welcomeMessage: Чем могу помочь?
77
+ placeholder: Задайте вопрос…
65
78
  logoUrl: null
66
79
  session:
80
+ # Pin every chat to a directory (optional, one of):
81
+ # cwd: "D:/qa-docs" # direct absolute directory pin
82
+ # workspaceId: "<uuid>" # or a registered DSH workspace
67
83
  policy: browser-persistent
68
- storageKey: dsh-qa-surface
84
+ storageKey: dsh-qa-surface.session
69
85
  workspaceId: company-knowledge
70
86
  fixedSessionId: null
71
87
  agentPreset: qa-assistant
@@ -74,19 +90,75 @@ config:
74
90
  reasoningEffort: null
75
91
  ui:
76
92
  showHeader: true
77
- showReset: true
93
+ showReset: false
78
94
  showStop: true
79
95
  showTimestamps: false
80
96
  showToolActivity: false
81
97
  showReasoning: false
82
98
  renderMarkdown: true
83
99
  maxContentWidth: 900
100
+ showSessionList: false
101
+ accounts:
102
+ enabled: false
103
+ allowRegistration: true
104
+ sessionTtlDays: 30
105
+ showOtherUsersChats: false
84
106
  suggestedQuestions:
85
- - How do I request access?
86
- - Where is the runbook?
107
+ - Как запросить доступ?
108
+ - Где лежит инструкция?
87
109
  interaction:
88
110
  approvals: blocked
89
111
  questions: unsupported
112
+ lockdown:
113
+ enabled: true
114
+ enforceFixedAgentPreset: true
115
+ enforceFixedWorkspace: true
116
+ enforceFixedModel: true
117
+ sandboxMode: read-only
118
+ approvalPolicy: never
119
+ permissionPreset: qa-read-only
120
+ allowPermissionChanges: false
121
+ allowSlashCommands: false
122
+ allowSettingsMutation: false
123
+ allowSessionReset: false
124
+ allowSessionRename: false
125
+ allowSessionDelete: false
126
+ allowArbitrarySessionOpen: false
127
+ toolPolicy:
128
+ mode: allow-list
129
+ allow: []
130
+ sources:
131
+ enabled: true
132
+ collect:
133
+ parentAgent: true
134
+ subagents: true
135
+ persistTurnEvent: true
136
+ display:
137
+ sidebar: true
138
+ footer: true
139
+ groupByKind: true
140
+ showDiscovered: false
141
+ showOriginBadges: false
142
+ maxInitiallyVisiblePerGroup: 8
143
+ webSearch:
144
+ promoteSearchResultsWithoutFetch: true
145
+ maxPromotedPerSearch: 5
146
+ dedupe:
147
+ normalizeUrls: true
148
+ stripTrackingParams: true
149
+ mergeFileRanges: true
150
+ filePreview:
151
+ enabled: true
152
+ markdownRenderedByDefault: true
153
+ allowRawToggle: true
154
+ maxBytes: 2000000
155
+ maxMarkdownRenderBytes: 1000000
156
+ subagents:
157
+ inheritSources: true
158
+ enableReportToolFallback: true
159
+ markIncompleteOpaqueRuns: true
160
+ legacy:
161
+ parseAssistantSourcesBlock: false
90
162
  ```
91
163
 
92
164
  `workspaceId` is recommended for a deterministic assistant. Without it, DSH
@@ -95,8 +167,12 @@ skills, knowledge connections and permission policy in `agentPreset`, not in
95
167
  this UI plugin.
96
168
 
97
169
  Model override is opt-in: `provider` and `model` must be set together. Slash
98
- commands are rejected as plain QA input in this MVP. `showReasoning: true` is
99
- also rejected; raw reasoning is intentionally outside the safe MVP surface.
170
+ commands are rejected as plain QA input. Reasoning and tool details remain
171
+ hidden by default. Enabling `ui.showReasoning` and `ui.showToolActivity` adds a
172
+ turn-scoped work disclosure: it stays open while the assistant is working,
173
+ then collapses to `Worked for ...` before the final answer. Tool capability is
174
+ still controlled exclusively by `lockdown.toolPolicy.allow`; the display flags
175
+ do not grant tools.
100
176
 
101
177
  Session policies:
102
178
 
@@ -106,8 +182,113 @@ Session policies:
106
182
  - `fixed` requires `fixedSessionId`, never creates a replacement, and is meant
107
183
  only for controlled single-user deployments.
108
184
 
109
- New chat creates another DSH Session and leaves the old one intact for operator
110
- inspection.
185
+ New chat is disabled by default. To expose it, set `lockdown.allowSessionReset:
186
+ true` plus either `ui.showReset: true` (header button) or `ui.showSessionList:
187
+ true` (sidebar button). Pressing it opens a draft composer and creates nothing:
188
+ the DSH Session is materialized lazily by the first prompt, so the chat list
189
+ stays quiet until a message is actually sent, and the old session stays intact
190
+ for operator inspection. The sidebar orders chats by the host's last update,
191
+ so merely opening a chat never moves it.
192
+
193
+ Regeneration: the last committed answer offers a retry action. The session log
194
+ is append-only, so "regenerate" sends a hidden instruction as an ordinary
195
+ prompt and the answer arrives as a follow-up turn; the projection hides that
196
+ instruction and the consecutive turns read as variants of one question,
197
+ navigable with a `< 2/2 >` switcher (newest shown by default).
198
+
199
+ Sources are structured Host-owned provenance, independent of
200
+ `ui.showToolActivity`. Successful reads/fetches, bounded web-search evidence,
201
+ Jira/Confluence/knowledge results, and inherited subagent sources are
202
+ normalized and deduplicated into one turn bundle. That exact bundle feeds the
203
+ answer footer and grouped drawer and is persisted as `qa/sources`, so reload
204
+ does not rerun tools. Search-only discovery stays hidden by default.
205
+
206
+ Local file cards open a source-scoped, read-only preview after Host-side real
207
+ path validation against the attested session root. Markdown opens rendered by
208
+ default with an HTML-free renderer and offers `Rendered / Raw`; raw mode jumps
209
+ to recorded line ranges. The endpoint cannot browse or write files and refuses
210
+ paths that are not evidence in the canonical bundle.
211
+
212
+ Observable local subagents are inherited recursively. The internal
213
+ `qa_report_sources` tool covers opaque delegated providers and is admitted as
214
+ a provenance-only capability even when it is not listed among ordinary QA
215
+ tools. A provider that neither exposes events nor reports sources marks the
216
+ turn provenance incomplete. The QA prompt tells models not to append a manual
217
+ `Sources`/`Источники` bibliography.
218
+
219
+ Images: the composer accepts PNG/JPEG/WebP/GIF via drag & drop onto the
220
+ composer, paste, and the picker button, several at once (soft client caps:
221
+ 8 images, 15 MB each). Images ride the prompt as base64 uploads the Host
222
+ promotes to durable attachments, so they survive reloads; sent images
223
+ render as clickable thumbnails on the message. Whether the model can see
224
+ them depends on the deployment's model (vision).
225
+
226
+ Subagents: the deployment may opt the delegation family (`subagent`,
227
+ `subagent_fork`, `send_message`, `list_agents`, `interrupt_agent`) into the
228
+ lockdown allow-list; the preset must mount them. Launches then render as
229
+ first-class work items (description, background flag, durable child id),
230
+ settlement notices appear as status rows, an "Агенты" header drawer lists the
231
+ chat's subagents with live status, and any subagent opens as a read-only
232
+ live transcript (composer disabled, one click back to the chat) - viewing
233
+ never attests or writes.
234
+
235
+ `ui.showSessionList: true` renders a minimal chat-history sidebar beside the
236
+ conversation. By default it lists only the current user's chats: the
237
+ client keeps a per-browser id index under
238
+ `<storageKey>:v1:<route>:chats` in localStorage (capped at 50, most recently
239
+ used first) and intersects it with the Host session list, so users sharing the
240
+ deployment never see each other's chats. An admin can explicitly enable
241
+ `accounts.showOtherUsersChats: true` to add chats owned by other QA accounts,
242
+ grouped by owner. Switching re-runs the full policy
243
+ attestation; a chat the Host no longer lists is pruned from the index. Each
244
+ row carries a two-click delete control that removes the chat from this
245
+ browser's index; deleting the chat that is currently open continues in a
246
+ fresh attested session. Host-side sessions are not deleted — DSH 0.1.x
247
+ exposes no session-deletion seam. The sidebar hides below 600px viewports.
248
+
249
+ The sidebar footer shows the deployed plugin version. Clicking it opens a
250
+ changelog dialog with a curated per-version summary (features and fixes);
251
+ Escape or a backdrop click closes it.
252
+
253
+ On `/qa`, the plugin shadows DSH's stock `welcome-notice` onboarding entry and
254
+ renders a route-owned Russian testing disclosure. Keeping the visible dialog
255
+ in the QA overlay prevents DSH's blank-session onboarding lifecycle from
256
+ dismissing it when a question is submitted. It explains the DeepSeek Harness
257
+ preview foundation, QA review of questions and answers, the work-related
258
+ scope, and local in-contour model processing. Explicit acknowledgement is
259
+ stored as a versioned browser-local flag; changing the disclosure version
260
+ shows it again. Other DSH routes retain the stock onboarding entry.
261
+
262
+ Locked mode requires a deployment permission preset named `qa-read-only`.
263
+ Extend the existing `@deepseek-ai/dsh-permission-presets` row without changing
264
+ its process-wide default:
265
+
266
+ ```yaml
267
+ - id: permission
268
+ name: "@deepseek-ai/dsh-permission-presets"
269
+ config:
270
+ presets:
271
+ read-only:
272
+ sandbox: read-only
273
+ approval: ask
274
+ workspace-write:
275
+ sandbox: workspace-write
276
+ approval: ask
277
+ danger-full-access:
278
+ sandbox: danger-full-access
279
+ approval: never
280
+ qa-read-only:
281
+ sandbox: read-only
282
+ approval: never
283
+ name: QA Read Only
284
+ description: No filesystem mutations and no permission escalation.
285
+ ```
286
+
287
+ The shipped tool allow-list is empty. Add only reviewed read-only tool names
288
+ from the actual deployment. A name that is not registered fails closed. The
289
+ Host restriction retains exact allow-listed tools from the agent preset's
290
+ ancestor scope, and the additional execution guard also denies session-scoped
291
+ tools and `run_code` unless their exact names are allowed.
111
292
 
112
293
  ## Security and deployment
113
294
 
@@ -115,12 +296,54 @@ inspection.
115
296
  with the same deployment authentication and DSH browser trust policy as the
116
297
  operator UI. The plugin never auto-approves a permission request or answers an
117
298
  interactive agent question. Configure a least-privilege, non-interactive agent
118
- preset for end-user deployments.
299
+ preset for end-user deployments. On a shared Host, a user who can open `/`,
300
+ call the normal DSH APIs, or use another authenticated client can bypass the
301
+ QA presentation. Use a dedicated DSH process/identity and network boundary
302
+ when `/qa` must be a strong security boundary.
119
303
 
120
304
  The `embedding.frameAncestors` field is reserved deployment metadata in 0.1.x;
121
305
  the plugin does not mutate CSP headers. Configure `frame-ancestors` at the
122
306
  trusted reverse proxy if iframe embedding is required.
123
307
 
308
+ ### Serving the QA rig over the LAN
309
+
310
+ The plugin itself needs no changes to be reachable from other machines; the
311
+ web server's bind address is a deployment composition decision. DSH refuses
312
+ `dsh --host 0.0.0.0` on the command line on purpose, so network exposure is
313
+ expressed with the shipped patch overlay instead:
314
+
315
+ ```bash
316
+ pnpm dsh --profile qa-surface --no-open --port 3082 \
317
+ --patch plugins/dsh-qa-surface/deploy/qa-lan.patch.yml
318
+ ```
319
+
320
+ The overlay sets the `webserver` row to `0.0.0.0` while keeping the `--port`
321
+ flag working. On an all-interfaces bind DSH derives trusted `/api` authorities
322
+ from the machine's LAN IPv4 addresses, so `http://<lan-ip>:3082/qa` works
323
+ without extra configuration; users reaching a DNS name instead of an IP
324
+ literal need `--trusted-host <name>` on the same command line.
325
+
326
+ LAN browsers cannot read the DSH settings namespace (settings RPCs are pinned
327
+ to loopback by the gateway). The plugin covers this itself: a browser that
328
+ sees the namespace as unavailable reads the effective configuration through
329
+ the `qaSurface/describe` Host Remote, so branding, session pinning and
330
+ lockdown UI switches keep working over the LAN. Host-side enforcement was
331
+ never dependent on that read path.
332
+
333
+ What a LAN deployment does not change:
334
+
335
+ - DSH has no authentication or TLS on this port; anyone who can reach it can
336
+ drive the harness, and the full operator UI at `/` stays reachable next to
337
+ `/qa`. Scope the port's reachability (subnet-limited firewall rule, VPN or
338
+ tailnet, authenticating reverse proxy) and use a dedicated process identity.
339
+ - Loopback-only settings, directory picking and credential RPCs stay refused
340
+ for LAN clients. On `/qa`, the plugin replaces DSH's non-persistent welcome
341
+ step with a QA-specific testing disclosure and remembers its exact copy
342
+ version in that browser's local storage. The stock DSH notice remains
343
+ unchanged on operator routes.
344
+
345
+ See [Configuration](docs/CONFIGURATION.md) for the config-channel details.
346
+
124
347
  See [Architecture](docs/ARCHITECTURE.md),
125
348
  [Configuration](docs/CONFIGURATION.md), and
126
349
  [Compatibility](docs/COMPATIBILITY.md).