@yadsh/dsh-qa-surface 0.2.0 → 0.4.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 (325) hide show
  1. package/README.md +236 -20
  2. package/compatibility.json +11 -3
  3. package/lib/account-remotes.js +100 -0
  4. package/lib/account-remotes.js.map +1 -0
  5. package/lib/accounts/credentials.js +27 -0
  6. package/lib/accounts/credentials.js.map +1 -0
  7. package/lib/accounts/errors.js +9 -0
  8. package/lib/accounts/errors.js.map +1 -0
  9. package/lib/accounts/file.js +101 -0
  10. package/lib/accounts/file.js.map +1 -0
  11. package/lib/accounts/index.js +2 -0
  12. package/lib/accounts/index.js.map +1 -0
  13. package/lib/accounts/store.js +461 -0
  14. package/lib/accounts/store.js.map +1 -0
  15. package/lib/accounts/token.js +61 -0
  16. package/lib/accounts/token.js.map +1 -0
  17. package/lib/attachment-rules.js +111 -0
  18. package/lib/attachment-rules.js.map +1 -0
  19. package/lib/attestation.js.map +1 -1
  20. package/lib/cli.js +256 -0
  21. package/lib/cli.js.map +1 -0
  22. package/lib/client/QaAccountsController.js +359 -0
  23. package/lib/client/QaAccountsController.js.map +1 -0
  24. package/lib/client/QaSessionController.js +238 -180
  25. package/lib/client/QaSessionController.js.map +1 -1
  26. package/lib/client/QaSurface.js +281 -105
  27. package/lib/client/QaSurface.js.map +1 -1
  28. package/lib/client/QaTranscriptAdapter.js +95 -244
  29. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  30. package/lib/client/attachments.js +150 -0
  31. package/lib/client/attachments.js.map +1 -0
  32. package/lib/client/attestation.js +7 -1
  33. package/lib/client/attestation.js.map +1 -1
  34. package/lib/client/base64.js +16 -0
  35. package/lib/client/base64.js.map +1 -0
  36. package/lib/client/chat-files.js +48 -0
  37. package/lib/client/chat-files.js.map +1 -0
  38. package/lib/client/components/Markdown.js +54 -13
  39. package/lib/client/components/Markdown.js.map +1 -1
  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 +131 -0
  43. package/lib/client/components/QaChangelog.js.map +1 -0
  44. package/lib/client/components/QaComposer.js +56 -74
  45. package/lib/client/components/QaComposer.js.map +1 -1
  46. package/lib/client/components/QaFileAttachment.js +19 -0
  47. package/lib/client/components/QaFileAttachment.js.map +1 -0
  48. package/lib/client/components/QaFilesPanel.js +45 -0
  49. package/lib/client/components/QaFilesPanel.js.map +1 -0
  50. package/lib/client/components/QaHeader.js +34 -0
  51. package/lib/client/components/QaHeader.js.map +1 -0
  52. package/lib/client/components/QaMessage.js +61 -18
  53. package/lib/client/components/QaMessage.js.map +1 -1
  54. package/lib/client/components/QaModal.js +31 -0
  55. package/lib/client/components/QaModal.js.map +1 -0
  56. package/lib/client/components/QaProfile.js +64 -0
  57. package/lib/client/components/QaProfile.js.map +1 -0
  58. package/lib/client/components/QaRightRail.js +15 -0
  59. package/lib/client/components/QaRightRail.js.map +1 -0
  60. package/lib/client/components/QaSidebar.js +68 -20
  61. package/lib/client/components/QaSidebar.js.map +1 -1
  62. package/lib/client/components/QaSourcesPanel.js +127 -0
  63. package/lib/client/components/QaSourcesPanel.js.map +1 -0
  64. package/lib/client/components/QaTurnRail.js +296 -0
  65. package/lib/client/components/QaTurnRail.js.map +1 -0
  66. package/lib/client/components/QaWelcomeNotice.js +125 -0
  67. package/lib/client/components/QaWelcomeNotice.js.map +1 -0
  68. package/lib/client/components/QaWidthHandle.js +147 -0
  69. package/lib/client/components/QaWidthHandle.js.map +1 -0
  70. package/lib/client/components/QaWorkGroup.js +19 -20
  71. package/lib/client/components/QaWorkGroup.js.map +1 -1
  72. package/lib/client/components/source-refs.js +78 -0
  73. package/lib/client/components/source-refs.js.map +1 -0
  74. package/lib/client/components/surface-utils.js +28 -0
  75. package/lib/client/components/surface-utils.js.map +1 -0
  76. package/lib/client/components/thinking-phrases.js +24 -0
  77. package/lib/client/components/thinking-phrases.js.map +1 -0
  78. package/lib/client/create-session.js +26 -0
  79. package/lib/client/create-session.js.map +1 -0
  80. package/lib/client/failure-copy.js +63 -0
  81. package/lib/client/failure-copy.js.map +1 -0
  82. package/lib/client/index.js +135 -16
  83. package/lib/client/index.js.map +1 -1
  84. package/lib/client/project-session-state.js +78 -0
  85. package/lib/client/project-session-state.js.map +1 -0
  86. package/lib/client/prompt-content.js +54 -0
  87. package/lib/client/prompt-content.js.map +1 -0
  88. package/lib/client/session-admission.js +38 -0
  89. package/lib/client/session-admission.js.map +1 -0
  90. package/lib/client/session-assets.js +52 -0
  91. package/lib/client/session-assets.js.map +1 -0
  92. package/lib/client/session-sources.js +61 -0
  93. package/lib/client/session-sources.js.map +1 -0
  94. package/lib/client/settings/card.js +143 -0
  95. package/lib/client/settings/card.js.map +1 -0
  96. package/lib/client/settings/fields.js +145 -0
  97. package/lib/client/settings/fields.js.map +1 -0
  98. package/lib/client/settings/format.js +255 -0
  99. package/lib/client/settings/format.js.map +1 -0
  100. package/lib/client/settings/sections/access.js +25 -0
  101. package/lib/client/settings/sections/access.js.map +1 -0
  102. package/lib/client/settings/sections/accounts.js +47 -0
  103. package/lib/client/settings/sections/accounts.js.map +1 -0
  104. package/lib/client/settings/sections/attachments.js +46 -0
  105. package/lib/client/settings/sections/attachments.js.map +1 -0
  106. package/lib/client/settings/sections/branding.js +28 -0
  107. package/lib/client/settings/sections/branding.js.map +1 -0
  108. package/lib/client/settings/sections/common.js +20 -0
  109. package/lib/client/settings/sections/common.js.map +1 -0
  110. package/lib/client/settings/sections/embedding.js +19 -0
  111. package/lib/client/settings/sections/embedding.js.map +1 -0
  112. package/lib/client/settings/sections/interface.js +60 -0
  113. package/lib/client/settings/sections/interface.js.map +1 -0
  114. package/lib/client/settings/sections/lockdown.js +82 -0
  115. package/lib/client/settings/sections/lockdown.js.map +1 -0
  116. package/lib/client/settings/sections/session.js +53 -0
  117. package/lib/client/settings/sections/session.js.map +1 -0
  118. package/lib/client/settings/sections/sources.js +72 -0
  119. package/lib/client/settings/sections/sources.js.map +1 -0
  120. package/lib/client/settings/sections/status.js +37 -0
  121. package/lib/client/settings/sections/status.js.map +1 -0
  122. package/lib/client/settings/sections.js +23 -0
  123. package/lib/client/settings/sections.js.map +1 -0
  124. package/lib/client/settings/styles.js +57 -0
  125. package/lib/client/settings/styles.js.map +1 -0
  126. package/lib/client/settlement.js +24 -0
  127. package/lib/client/settlement.js.map +1 -0
  128. package/lib/client/stream-publisher.js +47 -0
  129. package/lib/client/stream-publisher.js.map +1 -0
  130. package/lib/client/styles.js +225 -17
  131. package/lib/client/styles.js.map +1 -1
  132. package/lib/client/tool-presentation.js +102 -0
  133. package/lib/client/tool-presentation.js.map +1 -0
  134. package/lib/client/turn-sources.js +98 -0
  135. package/lib/client/turn-sources.js.map +1 -0
  136. package/lib/client/types.js +2 -0
  137. package/lib/client/types.js.map +1 -1
  138. package/lib/client/use-right-rail.js +96 -0
  139. package/lib/client/use-right-rail.js.map +1 -0
  140. package/lib/client/use-session-ui-state.js +75 -0
  141. package/lib/client/use-session-ui-state.js.map +1 -0
  142. package/lib/client/use-transcript-view.js +44 -0
  143. package/lib/client/use-transcript-view.js.map +1 -0
  144. package/lib/client.js +9052 -1345
  145. package/lib/client.js.map +1 -1
  146. package/lib/config-resolvers/accounts.js +60 -0
  147. package/lib/config-resolvers/accounts.js.map +1 -0
  148. package/lib/config-resolvers/attachments.js +28 -0
  149. package/lib/config-resolvers/attachments.js.map +1 -0
  150. package/lib/config-resolvers/basics.js +59 -0
  151. package/lib/config-resolvers/basics.js.map +1 -0
  152. package/lib/config-resolvers/defaults.js +132 -0
  153. package/lib/config-resolvers/defaults.js.map +1 -0
  154. package/lib/config-resolvers/lockdown.js +79 -0
  155. package/lib/config-resolvers/lockdown.js.map +1 -0
  156. package/lib/config-resolvers/session.js +42 -0
  157. package/lib/config-resolvers/session.js.map +1 -0
  158. package/lib/config-resolvers/shared.js +42 -0
  159. package/lib/config-resolvers/shared.js.map +1 -0
  160. package/lib/config-resolvers/sources.js +81 -0
  161. package/lib/config-resolvers/sources.js.map +1 -0
  162. package/lib/config-resolvers/ui.js +20 -0
  163. package/lib/config-resolvers/ui.js.map +1 -0
  164. package/lib/config.js +279 -102
  165. package/lib/config.js.map +1 -1
  166. package/lib/entry-redirect.js +45 -0
  167. package/lib/entry-redirect.js.map +1 -0
  168. package/lib/host-route.js +25 -1
  169. package/lib/host-route.js.map +1 -1
  170. package/lib/index.js +258 -23
  171. package/lib/index.js.map +1 -1
  172. package/lib/launch-token.js +42 -0
  173. package/lib/launch-token.js.map +1 -0
  174. package/lib/profile.js +214 -0
  175. package/lib/profile.js.map +1 -0
  176. package/lib/prompt-notes.js +231 -0
  177. package/lib/prompt-notes.js.map +1 -0
  178. package/lib/provenance/collector.js +40 -0
  179. package/lib/provenance/collector.js.map +1 -0
  180. package/lib/provenance/connectors.js +147 -0
  181. package/lib/provenance/connectors.js.map +1 -0
  182. package/lib/provenance/dedupe.js +89 -0
  183. package/lib/provenance/dedupe.js.map +1 -0
  184. package/lib/provenance/extractors.js +340 -0
  185. package/lib/provenance/extractors.js.map +1 -0
  186. package/lib/provenance/file-preview.js +39 -0
  187. package/lib/provenance/file-preview.js.map +1 -0
  188. package/lib/provenance/host-store.js +387 -0
  189. package/lib/provenance/host-store.js.map +1 -0
  190. package/lib/provenance/index.js +9 -0
  191. package/lib/provenance/index.js.map +1 -0
  192. package/lib/provenance/normalize.js +128 -0
  193. package/lib/provenance/normalize.js.map +1 -0
  194. package/lib/provenance/reported.js +53 -0
  195. package/lib/provenance/reported.js.map +1 -0
  196. package/lib/provenance/types.js +2 -0
  197. package/lib/provenance/types.js.map +1 -0
  198. package/lib/resolve-config.js +35 -249
  199. package/lib/resolve-config.js.map +1 -1
  200. package/lib/secure-session.js +122 -14
  201. package/lib/secure-session.js.map +1 -1
  202. package/lib/shared/session-key.js +18 -0
  203. package/lib/shared/session-key.js.map +1 -0
  204. package/lib/shared/settings.js +10 -0
  205. package/lib/shared/settings.js.map +1 -0
  206. package/lib/thinking-phrases.js +68 -0
  207. package/lib/thinking-phrases.js.map +1 -0
  208. package/lib/typert.host.js +749 -11
  209. package/lib/typert.remote-client.d.ts +23 -3
  210. package/lib/typert.remote-client.js +593 -6
  211. package/lib/types/account-remotes.d.ts +42 -0
  212. package/lib/types/accounts/credentials.d.ts +14 -0
  213. package/lib/types/accounts/errors.d.ts +11 -0
  214. package/lib/types/accounts/file.d.ts +82 -0
  215. package/lib/types/accounts/index.d.ts +2 -0
  216. package/lib/types/accounts/store.d.ts +144 -0
  217. package/lib/types/accounts/token.d.ts +31 -0
  218. package/lib/types/attachment-rules.d.ts +44 -0
  219. package/lib/types/attestation.d.ts +1 -1
  220. package/lib/types/cli.d.ts +9 -0
  221. package/lib/types/client/QaAccountsController.d.ts +102 -0
  222. package/lib/types/client/QaConfigController.d.ts +1 -1
  223. package/lib/types/client/QaSessionController.d.ts +61 -22
  224. package/lib/types/client/QaSurface.d.ts +14 -3
  225. package/lib/types/client/QaTranscriptAdapter.d.ts +5 -10
  226. package/lib/types/client/attachments.d.ts +54 -0
  227. package/lib/types/client/base64.d.ts +10 -0
  228. package/lib/types/client/chat-files.d.ts +30 -0
  229. package/lib/types/client/components/Markdown.d.ts +11 -1
  230. package/lib/types/client/components/QaAgentsDrawer.d.ts +1 -1
  231. package/lib/types/client/components/QaAuthGate.d.ts +15 -0
  232. package/lib/types/client/components/QaChangelog.d.ts +21 -0
  233. package/lib/types/client/components/QaComposer.d.ts +8 -4
  234. package/lib/types/client/components/QaFileAttachment.d.ts +14 -0
  235. package/lib/types/client/components/QaFilesPanel.d.ts +15 -0
  236. package/lib/types/client/components/QaHeader.d.ts +41 -0
  237. package/lib/types/client/components/QaMessage.d.ts +12 -3
  238. package/lib/types/client/components/QaModal.d.ts +26 -0
  239. package/lib/types/client/components/QaProfile.d.ts +25 -0
  240. package/lib/types/client/components/QaRightRail.d.ts +23 -0
  241. package/lib/types/client/components/QaSidebar.d.ts +44 -2
  242. package/lib/types/client/components/QaSourcesPanel.d.ts +23 -0
  243. package/lib/types/client/components/QaTurnRail.d.ts +65 -0
  244. package/lib/types/client/components/QaWelcomeNotice.d.ts +27 -0
  245. package/lib/types/client/components/QaWidthHandle.d.ts +43 -0
  246. package/lib/types/client/components/QaWorkGroup.d.ts +5 -3
  247. package/lib/types/client/components/source-refs.d.ts +16 -0
  248. package/lib/types/client/components/surface-utils.d.ts +11 -0
  249. package/lib/types/client/components/thinking-phrases.d.ts +9 -0
  250. package/lib/types/client/create-session.d.ts +13 -0
  251. package/lib/types/client/failure-copy.d.ts +19 -0
  252. package/lib/types/client/index.d.ts +1 -0
  253. package/lib/types/client/project-session-state.d.ts +30 -0
  254. package/lib/types/client/prompt-content.d.ts +30 -0
  255. package/lib/types/client/session-admission.d.ts +23 -0
  256. package/lib/types/client/session-assets.d.ts +20 -0
  257. package/lib/types/client/session-sources.d.ts +28 -0
  258. package/lib/types/client/settings/card.d.ts +23 -0
  259. package/lib/types/client/settings/fields.d.ts +139 -0
  260. package/lib/types/client/settings/format.d.ts +108 -0
  261. package/lib/types/client/settings/sections/access.d.ts +9 -0
  262. package/lib/types/client/settings/sections/accounts.d.ts +9 -0
  263. package/lib/types/client/settings/sections/attachments.d.ts +15 -0
  264. package/lib/types/client/settings/sections/branding.d.ts +8 -0
  265. package/lib/types/client/settings/sections/common.d.ts +28 -0
  266. package/lib/types/client/settings/sections/embedding.d.ts +8 -0
  267. package/lib/types/client/settings/sections/interface.d.ts +9 -0
  268. package/lib/types/client/settings/sections/lockdown.d.ts +9 -0
  269. package/lib/types/client/settings/sections/session.d.ts +9 -0
  270. package/lib/types/client/settings/sections/sources.d.ts +9 -0
  271. package/lib/types/client/settings/sections/status.d.ts +22 -0
  272. package/lib/types/client/settings/sections.d.ts +25 -0
  273. package/lib/types/client/settings/styles.d.ts +8 -0
  274. package/lib/types/client/settlement.d.ts +6 -0
  275. package/lib/types/client/stream-publisher.d.ts +28 -0
  276. package/lib/types/client/tool-presentation.d.ts +6 -0
  277. package/lib/types/client/turn-sources.d.ts +14 -0
  278. package/lib/types/client/types.d.ts +101 -9
  279. package/lib/types/client/use-right-rail.d.ts +56 -0
  280. package/lib/types/client/use-session-ui-state.d.ts +41 -0
  281. package/lib/types/client/use-transcript-view.d.ts +20 -0
  282. package/lib/types/config-resolvers/accounts.d.ts +15 -0
  283. package/lib/types/config-resolvers/attachments.d.ts +6 -0
  284. package/lib/types/config-resolvers/basics.d.ts +8 -0
  285. package/lib/types/config-resolvers/defaults.d.ts +4 -0
  286. package/lib/types/config-resolvers/lockdown.d.ts +10 -0
  287. package/lib/types/config-resolvers/session.d.ts +6 -0
  288. package/lib/types/config-resolvers/shared.d.ts +16 -0
  289. package/lib/types/config-resolvers/sources.d.ts +6 -0
  290. package/lib/types/config-resolvers/ui.d.ts +6 -0
  291. package/lib/types/entry-redirect.d.ts +20 -0
  292. package/lib/types/host-route.d.ts +14 -1
  293. package/lib/types/index.d.ts +54 -3
  294. package/lib/types/launch-token.d.ts +21 -0
  295. package/lib/types/profile.d.ts +76 -0
  296. package/lib/types/prompt-notes.d.ts +110 -0
  297. package/lib/types/provenance/collector.d.ts +26 -0
  298. package/lib/types/provenance/connectors.d.ts +11 -0
  299. package/lib/types/provenance/dedupe.d.ts +6 -0
  300. package/lib/types/provenance/extractors.d.ts +14 -0
  301. package/lib/types/provenance/file-preview.d.ts +10 -0
  302. package/lib/types/provenance/host-store.d.ts +54 -0
  303. package/lib/types/provenance/index.d.ts +10 -0
  304. package/lib/types/provenance/normalize.d.ts +13 -0
  305. package/lib/types/provenance/reported.d.ts +5 -0
  306. package/lib/types/provenance/types.d.ts +78 -0
  307. package/lib/types/resolve-config.d.ts +8 -3
  308. package/lib/types/secure-session.d.ts +36 -2
  309. package/lib/types/shared/session-key.d.ts +23 -0
  310. package/lib/types/shared/settings.d.ts +10 -0
  311. package/lib/types/thinking-phrases.d.ts +17 -0
  312. package/lib/types/types.d.ts +290 -16
  313. package/lib/types/user-workspace.d.ts +38 -0
  314. package/lib/user-workspace.js +231 -0
  315. package/lib/user-workspace.js.map +1 -0
  316. package/package.json +71 -47
  317. package/CHANGELOG.md +0 -93
  318. package/SPEC-dsh-qa-surface.md +0 -2425
  319. package/docs/ARCHITECTURE.md +0 -66
  320. package/docs/COMPATIBILITY.md +0 -14
  321. package/docs/CONFIGURATION.md +0 -112
  322. package/docs/EMBEDDING.md +0 -13
  323. package/lib/client/components/QaSourcesDrawer.js +0 -46
  324. package/lib/client/components/QaSourcesDrawer.js.map +0 -1
  325. package/lib/types/client/components/QaSourcesDrawer.d.ts +0 -8
@@ -1,2425 +0,0 @@
1
- # SPEC / Implementation Plan: `dsh-qa-surface`
2
-
3
- **Status:** Draft / implementation-ready
4
- **Date:** 2026-09-05
5
- **Last updated:** 2026-09-08 — added locked-down / read-only QA execution policy
6
- **Target:** DeepSeek Harness Web
7
- **Artifact type:** external DSH UI plugin with a dedicated `/qa` browser surface
8
-
9
- ---
10
-
11
- ## 1. Goal
12
-
13
- Create a DeepSeek Harness plugin named **`dsh-qa-surface`** that exposes a minimal, embeddable QA/chat interface at:
14
-
15
- ```text
16
- /qa
17
- ```
18
-
19
- The page must look like a standalone assistant rather than the standard DSH developer UI:
20
-
21
- - one conversation;
22
- - user and assistant messages;
23
- - text input;
24
- - Send / Stop;
25
- - optional reset/new chat;
26
- - optional welcome text and suggested questions;
27
- - no workspace tree;
28
- - no settings;
29
- - no model picker by default;
30
- - no tool-call tree;
31
- - no reasoning/trajectory UI;
32
- - no developer-oriented DSH controls.
33
-
34
- At the same time, the assistant must continue to run through the **real DSH runtime** and retain DSH capabilities such as:
35
-
36
- - Sessions;
37
- - Agent / Agent Loop;
38
- - model providers and routing;
39
- - system prompt and agent preset;
40
- - tools;
41
- - skills;
42
- - MCP;
43
- - memory/knowledge plugins;
44
- - subagents;
45
- - persistence;
46
- - telemetry;
47
- - permission policy;
48
- - other Host-side plugins composed into the selected agent/session.
49
-
50
- The plugin must **not** create a second agent runtime or proxy prompts to an LLM directly.
51
-
52
- ---
53
-
54
- ## 2. Primary design decision
55
-
56
- ### 2.1 MVP implementation
57
-
58
- Implement `/qa` as a **full-screen browser UI plugin occupying the additive `shell.overlay` slot** when `window.location.pathname` matches the configured QA route.
59
-
60
- The normal DSH Web application continues to boot underneath it.
61
-
62
- ```text
63
- GET /qa
64
- │
65
- ▼
66
- DSH frontend-static SPA fallback
67
- │
68
- ▼
69
- standard DSH index.html + window.__DSH_BOOT__
70
- │
71
- ▼
72
- normal browser plugin graph
73
- │
74
- ├── ui-layout
75
- ├── ui-conversation
76
- ├── client-runtime
77
- ├── connection
78
- └── dsh-qa-surface
79
- │
80
- └── shell.overlay → fullscreen QA surface
81
- ```
82
-
83
- This is intentionally preferred over replacing DSH's entire `root`, `sidebar`, or `conversation` slots.
84
-
85
- ### Why
86
-
87
- Current DSH layout exposes the frame-level seats:
88
-
89
- - `sidebar` — `single`, root scope;
90
- - `conversation` — `single`, session-maybe scope;
91
- - `details` — `single`, session scope;
92
- - `shell.overlay` — additive `list`, root scope.
93
-
94
- There is currently no first-class generic "global page" route/slot for third-party plugins. Replacing `sidebar`, `conversation`, or the entire root would make the plugin compete with first-party occupants and is more likely to break on DSH updates.
95
-
96
- `conversation.session` is useful for replacing the body of a selected session, but by itself it does not provide a top-level page and does not remove the surrounding frame.
97
-
98
- Therefore MVP should use the additive full-frame overlay and keep all host/runtime services native.
99
-
100
- ### 2.2 Future implementation
101
-
102
- Once the plugin stabilizes, optionally add a second delivery mode:
103
-
104
- ```text
105
- qa profile / bundle
106
- ├── dsh-base
107
- ├── web transport
108
- ├── client runtime
109
- └── qa-only browser roster
110
- ```
111
-
112
- That profile can remove unused first-party UI plugins entirely. It is **not required for MVP**.
113
-
114
- ---
115
-
116
- ## 3. Verified DSH architecture assumptions
117
-
118
- The implementation plan below relies on the following current DSH behavior as of 2026-09-05.
119
-
120
- ### 3.1 DSH client UI is plugin-based
121
-
122
- A browser UI plugin is a dual-face package:
123
-
124
- ```text
125
- src/index.ts Host / Node half
126
- src/client/index.ts Browser half
127
- ```
128
-
129
- The package declares a `dsh.client` manifest and exports `./client`.
130
-
131
- The Host-side client module registry discovers enabled Loader entries with `dsh.client`, serves their bundles under `/plugins/...`, and injects the client module graph into `window.__DSH_BOOT__`.
132
-
133
- ### 3.2 `/qa` does not need a separate static server
134
-
135
- The shipped `dsh-host-frontend-static` owns the webserver fallback seat.
136
-
137
- For GET/HEAD requests, a missing static path falls back to `index.html` with HTTP 200. Therefore:
138
-
139
- ```text
140
- GET /qa
141
- ```
142
-
143
- can already boot the same DSH SPA shell without adding a new Vite server or replacing the fallback handler.
144
-
145
- ### 3.3 Session Controller remains authoritative
146
-
147
- Session lifecycle, prompt execution, history and live events remain Host-owned. The client runtime mirrors them and exposes session objects/snapshots.
148
-
149
- The QA surface must use this existing object/API layer instead of inventing a second transcript state machine.
150
-
151
- ### 3.4 Do not import private first-party React components
152
-
153
- Cross-plugin cooperation should happen through:
154
-
155
- - Cordis services;
156
- - typed slots;
157
- - public client/runtime contracts;
158
- - public Session/Conversation faces.
159
-
160
- Do **not** deep-import internal DSH components such as private `MessageItem`, private composer internals, or AppFrame implementation files.
161
-
162
- ### 3.5 DSH is still developer-preview software
163
-
164
- Compatibility-breaking changes are expected upstream.
165
-
166
- All DSH-specific calls must therefore be isolated behind small adapter/controller modules inside this plugin.
167
-
168
- ---
169
-
170
- ## 4. User-facing behavior
171
-
172
- ### 4.1 `/`
173
-
174
- Normal DSH Web remains untouched.
175
-
176
- ```text
177
- https://dsh.example.com/
178
- ```
179
-
180
- shows the standard DSH developer UI.
181
-
182
- ### 4.2 `/qa`
183
-
184
- ```text
185
- https://dsh.example.com/qa
186
- ```
187
-
188
- shows only the QA assistant surface.
189
-
190
- Example:
191
-
192
- ```text
193
- ┌───────────────────────────────────────────────┐
194
- │ Acme Assistant │
195
- │ Ask a question about the internal platform. │
196
- ├───────────────────────────────────────────────┤
197
- │ │
198
- │ Assistant │
199
- │ Hi. How can I help? │
200
- │ │
201
- │ User │
202
- │ How do I reset...? │
203
- │ │
204
- │ Assistant │
205
- │ To reset... │
206
- │ │
207
- ├───────────────────────────────────────────────┤
208
- │ Ask a question... Send │
209
- └───────────────────────────────────────────────┘
210
- ```
211
-
212
- No first-party DSH navigation or admin controls should be visible through the QA surface.
213
-
214
- ---
215
-
216
- ## 5. Scope
217
-
218
- ### MVP must include
219
-
220
- - `/qa` route recognition;
221
- - full-screen QA overlay;
222
- - responsive layout;
223
- - automatic creation/restoration of one DSH Session;
224
- - text prompts;
225
- - streamed assistant text;
226
- - persisted transcript through DSH Session persistence;
227
- - loading/running state;
228
- - Stop/Cancel;
229
- - New Chat / Reset;
230
- - Markdown rendering;
231
- - basic errors and reconnect states;
232
- - configurable title/welcome/placeholder;
233
- - configurable fixed workspace/session target policy;
234
- - configurable agent preset;
235
- - configurable model override if explicitly set;
236
- - **locked-down QA mode enabled by default**;
237
- - fixed agent preset with no QA-user agent switching;
238
- - forced `read-only` sandbox mode;
239
- - forced `approval=never` / no permission escalation;
240
- - allow-list based tool exposure for QA sessions;
241
- - no model/workspace/permission/mode/settings switching from the QA surface;
242
- - safe handling of unsupported interactive states;
243
- - no leakage of tool calls/reasoning into the QA transcript by default;
244
- - same-origin use of the normal DSH connection/API stack;
245
- - desktop and mobile layout;
246
- - package build/install instructions;
247
- - tests.
248
-
249
- ### Post-MVP candidates
250
-
251
- - image/file attachments;
252
- - suggested-question chips;
253
- - source/citation cards;
254
- - compact tool-status indicators;
255
- - explicit `ask_user_question` support;
256
- - explicit approval UI;
257
- - conversation rating/feedback;
258
- - custom branding/logo;
259
- - iframe embedding helper;
260
- - embeddable JS launcher/widget;
261
- - per-user session mapping;
262
- - anonymous guest sessions behind a dedicated auth gateway;
263
- - dedicated `qa` DSH profile/bundle;
264
- - multiple assistant presets selected by URL;
265
- - public conversation links.
266
-
267
- ---
268
-
269
- ## 6. Non-goals
270
-
271
- The MVP must **not**:
272
-
273
- - fork DeepSeek Harness;
274
- - modify DSH built frontend files after every update;
275
- - start a second Vite/React server for production;
276
- - implement its own LLM provider calls;
277
- - duplicate DSH Agent Loop;
278
- - duplicate DSH Session persistence;
279
- - bypass DSH browser trust/auth mechanisms;
280
- - enable unrestricted CORS;
281
- - automatically approve permission requests;
282
- - automatically answer agent questions;
283
- - expose admin/settings APIs to unauthenticated users;
284
- - rely on hidden/disabled UI controls as the only security boundary;
285
- - allow the QA user to switch agent preset, workspace, model, sandbox mode, approval policy, or permission preset;
286
- - expose a general-purpose shell/terminal/code-execution tool in the default locked-down preset;
287
- - expose mutable external-service tools by default;
288
- - expose raw reasoning by default;
289
- - expose raw tool arguments/results by default;
290
- - depend on private DSH component implementation details;
291
- - take over first-party `sidebar`, `conversation`, `details`, or `root` seats globally.
292
-
293
- ---
294
-
295
- ## 7. Package name
296
-
297
- Preferred npm/package name:
298
-
299
- ```text
300
- dsh-qa-surface
301
- ```
302
-
303
- If using an npm scope:
304
-
305
- ```text
306
- @<scope>/dsh-qa-surface
307
- ```
308
-
309
- Repository topic:
310
-
311
- ```text
312
- dsh-plugin
313
- ```
314
-
315
- ---
316
-
317
- ## 8. Proposed repository structure
318
-
319
- ```text
320
- dsh-qa-surface/
321
- ├── package.json
322
- ├── README.md
323
- ├── LICENSE
324
- ├── CHANGELOG.md
325
- ├── tsconfig.json
326
- ├── tsconfig.host.json
327
- ├── tsconfig.client.json
328
- ├── tsdown.config.ts
329
- ├── cordis.patch.yml # example install/composition patch
330
- ├── docs/
331
- │ ├── ARCHITECTURE.md
332
- │ ├── CONFIGURATION.md
333
- │ ├── EMBEDDING.md
334
- │ └── COMPATIBILITY.md
335
- ├── src/
336
- │ ├── index.ts # Host half
337
- │ ├── config.ts
338
- │ ├── settings.ts
339
- │ ├── invariant.ts
340
- │ └── client/
341
- │ ├── index.ts # browser apply()/inject
342
- │ ├── QaSurface.tsx
343
- │ ├── QaSessionController.ts
344
- │ ├── QaRouteController.ts
345
- │ ├── QaConfigController.ts
346
- │ ├── QaTranscriptAdapter.ts
347
- │ ├── components/
348
- │ │ ├── QaHeader.tsx
349
- │ │ ├── QaTranscript.tsx
350
- │ │ ├── QaMessage.tsx
351
- │ │ ├── QaComposer.tsx
352
- │ │ ├── QaStatus.tsx
353
- │ │ ├── QaError.tsx
354
- │ │ └── SuggestedQuestions.tsx
355
- │ ├── stores/
356
- │ │ ├── qa-route-store.ts
357
- │ │ ├── qa-session-store.ts
358
- │ │ └── qa-config-store.ts
359
- │ ├── styles/
360
- │ │ └── qa-surface.module.css
361
- │ └── types.ts
362
- └── test/
363
- ├── config.test.ts
364
- ├── route.test.ts
365
- ├── session-controller.test.ts
366
- ├── transcript-adapter.test.ts
367
- ├── client-smoke.test.tsx
368
- └── e2e/
369
- ├── qa-route.spec.ts
370
- ├── qa-stream.spec.ts
371
- ├── qa-reload.spec.ts
372
- └── qa-error.spec.ts
373
- ```
374
-
375
- The exact external-build tsdown setup may need to mirror DSH's lazy-CJS client bundle format because DSH does not currently publish a general external client build preset.
376
-
377
- ---
378
-
379
- ## 9. `package.json` requirements
380
-
381
- The package must expose both halves.
382
-
383
- Conceptual shape:
384
-
385
- ```json
386
- {
387
- "name": "dsh-qa-surface",
388
- "type": "module",
389
- "exports": {
390
- ".": {
391
- "types": "./lib/types/index.d.ts",
392
- "default": "./lib/index.js"
393
- },
394
- "./client": {
395
- "types": "./lib/types/client/index.d.ts",
396
- "default": "./lib/client.js"
397
- },
398
- "./package.json": "./package.json"
399
- },
400
- "dsh": {
401
- "client": {
402
- "platform": "web",
403
- "inject": [
404
- "@deepseek-ai/dsh-client-runtime",
405
- "@deepseek-ai/dsh-client-ui-layout",
406
- "@deepseek-ai/dsh-client-ui-theme"
407
- ]
408
- }
409
- }
410
- }
411
- ```
412
-
413
- The exact dependency edge list must be verified against the installed DSH version during implementation.
414
-
415
- Do not add dependencies merely to obtain private UI components.
416
-
417
- ---
418
-
419
- ## 10. Host half
420
-
421
- The Host half should be intentionally small.
422
-
423
- Responsibilities:
424
-
425
- 1. declare and validate plugin config;
426
- 2. register a DSH settings namespace for user-configurable values;
427
- 3. expose only the sanitized config required by the browser through supported DSH settings/client mechanisms;
428
- 4. optionally register plugin diagnostics/invariant information;
429
- 5. do not create sessions at startup;
430
- 6. do not create another HTTP server;
431
- 7. do not register a second SPA fallback;
432
- 8. do not change `/api` transport security.
433
-
434
- ### 10.1 Config source
435
-
436
- Use standard DSH/Cordis composition config as defaults and integrate with the DSH settings subsystem where practical.
437
-
438
- Preferred namespace:
439
-
440
- ```text
441
- qa-surface
442
- ```
443
-
444
- The browser boot graph itself contains package ids/URLs/dependency edges, not arbitrary plugin config. Therefore do **not** assume Host Cordis config magically arrives as arguments to the browser half.
445
-
446
- Preferred config transport:
447
-
448
- - Host registers a normal settings namespace;
449
- - Browser reads it through the supported DSH settings/client service;
450
- - settings writes remain subject to normal DSH permissions/trust.
451
-
452
- Avoid creating an unauthenticated `/qa-config.json` endpoint.
453
-
454
- ### 10.2 LAN config channel
455
-
456
- DSH pins settings RPCs to loopback (`settings.*` sits in the gateway's
457
- privileged method set), so a browser served from a non-loopback bind always
458
- sees the namespace as `unavailable`. The Host half therefore exposes one
459
- additional read-only Remote, `qaSurface/describe`, returning the effective
460
- resolved configuration — the same projection `secureSession` attests against.
461
- It is the network-neutral answer to responsibility 3: still plugin-scoped,
462
- still no arbitrary Host config, no secrets, no file paths beyond the
463
- configured ids, and no admission authority (enforcement stays in
464
- `secureSession`).
465
-
466
- Client contract: when the settings namespace is `unavailable` — and only
467
- then — the browser calls `describe` once per controller lifetime, waits in
468
- `loading`, and adopts the returned configuration as `ready`; a rejected or
469
- invalid answer falls back to the client defaults under `unavailable`. A
470
- readable namespace always wins and keeps delivering live updates.
471
-
472
- LAN serving itself is a deployment composition concern, not plugin config:
473
- the shipped `deploy/qa-lan.patch.yml` overlay binds the `webserver` row to
474
- `0.0.0.0`, and the DSH `/api` browser-trust fence derives the LAN authorities
475
- on its own. See the README "Serving the QA rig over the LAN" section for the
476
- deployment responsibilities this creates.
477
-
478
- ---
479
-
480
- ## 11. Configuration model
481
-
482
- Proposed initial schema:
483
-
484
- ```yaml
485
- enabled: true
486
-
487
- route:
488
- path: /qa
489
- matchChildren: true
490
-
491
- branding:
492
- title: Помощник
493
- subtitle: ""
494
- welcomeMessage: "Чем могу помочь?"
495
- placeholder: "Задайте вопрос…"
496
- logoUrl: null
497
-
498
- session:
499
- policy: browser-persistent
500
- storageKey: dsh-qa-surface.session
501
- workspaceId: null
502
- fixedSessionId: null
503
- agentPreset: null
504
- provider: null
505
- model: null
506
- reasoningEffort: null
507
-
508
- ui:
509
- showHeader: true
510
- showReset: false
511
- showStop: true
512
- showTimestamps: false
513
- showToolActivity: false
514
- showReasoning: false
515
- renderMarkdown: true
516
- maxContentWidth: 900
517
-
518
- suggestedQuestions:
519
- - "Что ты умеешь?"
520
- - "С чего начать?"
521
- - "Помоги разобраться с ошибкой"
522
-
523
- interaction:
524
- approvals: blocked
525
- questions: unsupported
526
-
527
- lockdown:
528
- enabled: true
529
- enforceFixedAgentPreset: true
530
- enforceFixedWorkspace: true
531
- enforceFixedModel: true
532
- sandboxMode: read-only
533
- approvalPolicy: never
534
- permissionPreset: qa-read-only
535
- allowPermissionChanges: false
536
- allowSlashCommands: false
537
- allowSettingsMutation: false
538
- allowSessionReset: false
539
- allowSessionRename: false
540
- allowSessionDelete: false
541
- allowArbitrarySessionOpen: false
542
- toolPolicy:
543
- mode: allow-list
544
- allow: []
545
-
546
- embedding:
547
- frameAncestors: null
548
- ```
549
-
550
- ### 11.1 `route.path`
551
-
552
- Default:
553
-
554
- ```text
555
- /qa
556
- ```
557
-
558
- Normalize:
559
-
560
- - must start with `/`;
561
- - remove trailing slash except root;
562
- - reject `/api` and `/plugins` prefixes;
563
- - reject empty strings.
564
-
565
- ### 11.2 Session policies
566
-
567
- Support three policy names in schema, but MVP only needs to fully implement the first two.
568
-
569
- #### `browser-persistent` — default
570
-
571
- - first visit creates a DSH Session;
572
- - store its id in browser storage;
573
- - reload/revisit restores it if the Host still knows the Session;
574
- - invalid/missing session id creates a replacement;
575
- - Reset creates a new Session and replaces the stored id.
576
-
577
- #### `new-on-load`
578
-
579
- - every fresh page load creates a new Session;
580
- - SPA navigation within the same page may keep it until leaving `/qa`;
581
- - suitable for kiosk/demo use.
582
-
583
- #### `fixed`
584
-
585
- - use `fixedSessionId`;
586
- - intended only for controlled internal deployments;
587
- - must never silently create that id if access policy refuses it;
588
- - not recommended for multiple independent users.
589
-
590
- ### 11.3 Workspace binding
591
-
592
- For a deterministic QA assistant, a configured workspace is preferred.
593
-
594
- MVP config:
595
-
596
- ```yaml
597
- session:
598
- workspaceId: <configured DSH workspace id>
599
- ```
600
-
601
- If `workspaceId` is absent, implementation may fall back to DSH's normal recent/current workspace logic only when that behavior is explicitly documented in the README.
602
-
603
- Do not silently bind to an arbitrary workspace if multiple are present.
604
-
605
- ### 11.4 Agent preset
606
-
607
- If configured:
608
-
609
- ```yaml
610
- session:
611
- agentPreset: qa-assistant
612
- ```
613
-
614
- new QA sessions must use that preset.
615
-
616
- This is the preferred place to encode:
617
-
618
- - QA system prompt;
619
- - skills;
620
- - tools;
621
- - knowledge integrations;
622
- - permission policy;
623
- - model policy.
624
-
625
- Do not stuff all assistant behavior into the UI plugin.
626
-
627
- ### 11.5 Model selection
628
-
629
- Default:
630
-
631
- ```yaml
632
- provider: null
633
- model: null
634
- ```
635
-
636
- means use DSH deployment/session defaults.
637
-
638
- If both are configured, apply the model selection to the QA session through the public session model-selection path.
639
-
640
- In locked-down mode, never render a model picker and do not expose a controller action for model switching. If provider/model are left `null`, operator deployment defaults may still determine the model for newly created sessions; that is an operator-side policy change, not a QA-user choice.
641
-
642
-
643
-
644
- ### 11.6 Locked-down execution policy
645
-
646
- `/qa` must default to a **capability-reduced execution profile**, not merely a visually simplified UI.
647
-
648
- The desired invariant is:
649
-
650
- ```text
651
- QA user can:
652
- - submit text questions;
653
- - receive assistant answers;
654
- - stop the current turn;
655
- - optionally start a fresh QA session.
656
-
657
- QA user cannot:
658
- - change the agent preset;
659
- - change workspace/cwd;
660
- - change model/provider/reasoning mode;
661
- - change permissions/sandbox/approval policy;
662
- - invoke DSH slash commands;
663
- - mutate settings;
664
- - open arbitrary existing sessions;
665
- - rename/delete sessions;
666
- - gain a shell/terminal merely because it exists globally;
667
- - trigger write-capable Jira/GitHub/Confluence/Slack/email/etc. actions;
668
- - escalate from read-only to a wider sandbox mode.
669
- ```
670
-
671
- This must be enforced in layers. Hiding selectors is only the presentation layer.
672
-
673
- #### Layer 1 — fixed session composition
674
-
675
- For every newly created QA session:
676
-
677
- - use exactly the configured `session.agentPreset`;
678
- - use exactly the configured `session.workspaceId`;
679
- - if model/provider are configured, use exactly those values;
680
- - refuse to silently fall back to another agent/workspace when the configured value is missing;
681
- - persist the selected QA session id, but do not persist user-selectable composition overrides.
682
-
683
- DSH currently treats the agent preset as a per-session composition fact. Existing upstream behavior already prevents changing the preset after a session has produced history; blank sessions may still have a preset choice before the first turn. Therefore the QA controller must create/bind the session with the configured preset and never expose the blank-session preset selector.
684
-
685
- Reference:
686
-
687
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-agent-preset/README.md
688
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md
689
-
690
- #### Layer 2 — force `read-only` + `approval=never`
691
-
692
- The locked-down QA policy must pin both independent DSH permission knobs:
693
-
694
- ```yaml
695
- sandbox: read-only
696
- approval: never
697
- ```
698
-
699
- Prefer defining a named deployment preset:
700
-
701
- ```yaml
702
- permissionPresets:
703
- presets:
704
- qa-read-only:
705
- sandbox: read-only
706
- approval: never
707
- name: QA Read Only
708
- description: No filesystem mutations and no permission escalation.
709
- ```
710
-
711
- On a **shared operator + QA DSH instance**, do not make `qa-read-only` the process-wide `defaultPreset` merely for this plugin, because that would also change the default permissions of ordinary operator-created sessions. Instead, the QA bootstrap path must select/pin `qa-read-only` on the freshly created blank QA session **before the first prompt is accepted**, then verify the effective sandbox/approval facts.
712
-
713
- On a **dedicated QA DSH instance**, setting `defaultPreset: qa-read-only` is recommended as an additional fail-safe.
714
-
715
- The exact surrounding Cordis row/config syntax must be adapted to the target deployment.
716
-
717
- Important: DSH's shipped permission preset table normally contains `workspace-write` and `danger-full-access`; `qa-read-only` is an explicit custom table entry for this deployment. The underlying knobs are the authoritative enforcement facts.
718
-
719
- `approval=never` is required because it deterministically rejects operations that request approval instead of presenting an approval UI. The QA surface must never implement an auto-approve path.
720
-
721
- References:
722
-
723
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/permission-presets.md
724
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/approval.md
725
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/sandbox/sandbox-policy/README.md
726
-
727
- #### Layer 3 — tool allow-list, not a deny-list
728
-
729
- `read-only` is **not equivalent to “no side effects.”** It governs file effects enforced by the DSH file sandbox; network and process visibility are outside that sandbox vocabulary. A shell command can still make network requests or trigger external side effects even when filesystem writes are denied.
730
-
731
- Therefore the default QA preset must use an explicit **allow-list of known read-only tools**.
732
-
733
- Conceptual policy:
734
-
735
- ```yaml
736
- lockdown:
737
- toolPolicy:
738
- mode: allow-list
739
- allow:
740
- - knowledge.search
741
- - knowledge.read
742
- - web.search
743
- - web.fetch
744
- ```
745
-
746
- The actual names must come from the composed DSH deployment. Do not copy these illustrative names blindly.
747
-
748
- Prefer allow-list semantics because newly installed global tools are then excluded automatically. DSH has a scoped `ToolRestriction.allow` mechanism for inherited global tools. Note that tools registered directly inside the agent's own scope are intentionally exempt from inherited restrictions, so **the QA agent preset itself must also be reviewed and must not register write-capable tools**.
749
-
750
- References:
751
-
752
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/tools.md
753
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts
754
-
755
- Default deny category examples:
756
-
757
- | Capability | Default QA policy | Reason |
758
- |---|---|---|
759
- | generic shell / bash / terminal | deny | can create non-filesystem side effects and broad host visibility |
760
- | generic code runtime | deny | same reason unless a separately constrained pure-compute runtime exists |
761
- | fs write/edit/move/delete | deny | direct mutation |
762
- | git commit/push/checkout/reset | deny | repository mutation / remote side effects |
763
- | Jira create/update/comment/transition | deny | external mutation |
764
- | Confluence create/update/delete | deny | external mutation |
765
- | Slack/email send | deny | external mutation |
766
- | browser/computer-use click/type/submit | deny | arbitrary external side effects |
767
- | settings/plugin management | deny | can expand capabilities |
768
- | permission/preset management | deny | can expand capabilities |
769
- | agent-preset management | deny | can change composition |
770
- | read-only KB/search/fetch tools | allow only after review | intended QA capability |
771
-
772
- For MCP, allow-list at the **tool level**, not merely at the server name. A server may expose both read and write actions.
773
-
774
- #### Layer 4 — no mutation commands or alternate control paths
775
-
776
- The QA composer must call the plain public prompt path directly and must not expose the general DSH command dispatcher.
777
-
778
- Default:
779
-
780
- ```yaml
781
- lockdown:
782
- allowSlashCommands: false
783
- ```
784
-
785
- At minimum prevent QA-side use of control paths equivalent to:
786
-
787
- ```text
788
- /permission ...
789
- /permissionPresets ...
790
- agent/preset selectors
791
- model selectors
792
- workspace selectors
793
- settings mutation
794
- plugin management
795
- ```
796
-
797
- Do not rely only on filtering strings that start with `/` if the DSH prompt API already provides a command-free prompt submission method. Prefer bypassing command adjudication entirely.
798
-
799
- #### Layer 5 — controller-side invariant checks
800
-
801
- `QaSessionController` must verify its effective session state before enabling Send.
802
-
803
- Conceptual check:
804
-
805
- ```ts
806
- interface QaLockdownInvariant {
807
- agentPresetMatches: boolean
808
- workspaceMatches: boolean
809
- modelMatches: boolean
810
- sandboxIsReadOnly: boolean
811
- approvalIsNever: boolean
812
- toolPolicyLoaded: boolean
813
- }
814
- ```
815
-
816
- If a required invariant cannot be proven, fail closed:
817
-
818
- ```text
819
- Assistant configuration is unavailable.
820
- ```
821
-
822
- and disable the composer. Do not continue under a wider or unknown policy.
823
-
824
- A mismatch should be operator-visible in logs with the expected and actual policy identifiers, but the QA page should not expose sensitive details.
825
-
826
- #### Layer 6 — host-side guard where public DSH seams allow it
827
-
828
- UI/controller checks protect normal use but are not enough against another client using the same authenticated DSH APIs. During implementation, use public Host-side DSH enforcement seams where available to bind QA-marked sessions to the locked policy.
829
-
830
- Preferred mechanisms, in order:
831
-
832
- 1. compose the QA preset so forbidden tools are not mounted at all;
833
- 2. apply scoped tool restrictions to inherited tools;
834
- 3. pin sandbox and approval facts before the first QA prompt;
835
- 4. prefer a Host-side QA session bootstrap operation that creates/binds the session and pins the QA permission facts in one controlled flow, if DSH exposes a clean public seam;
836
- 5. otherwise create a blank session, immediately select `qa-read-only`, verify it, and keep Send disabled until verification succeeds;
837
- 6. add a monotonic `tools/pre-execute` / execution guard for QA sessions if a second server-side side-effect check is needed;
838
- 7. never implement capability expansion through the QA plugin.
839
-
840
- Do not monkey-patch private RPC handlers merely to create a guard. If DSH has no public seam for a particular mutation path, document that limitation and move the boundary to deployment isolation rather than pretending the browser UI makes it secure.
841
-
842
- #### Layer 7 — deployment isolation for strong guarantees
843
-
844
- A `/qa` overlay on the same fully privileged DSH Host is **not a security boundary by itself**. If the same authenticated user can navigate to normal `/`, call privileged DSH APIs, or use another DSH client, hiding controls on `/qa` does not revoke those Host capabilities.
845
-
846
- For an internal convenience UI, the layered session/tool policy above may be enough.
847
-
848
- For untrusted or semi-trusted QA users, prefer one of:
849
-
850
- ```text
851
- Best isolation:
852
- dedicated QA DSH instance/container
853
- + only QA preset/tools mounted
854
- + read-only corpus mounts
855
- + no developer credentials
856
- + restricted network egress
857
-
858
- Good isolation:
859
- separate QA auth role/gateway
860
- + Host-side API authorization for mutable capabilities
861
- + locked QA preset/tool registry
862
-
863
- Weakest:
864
- same privileged DSH Host/browser identity
865
- + UI controls merely hidden
866
- ```
867
-
868
- DSH upstream explicitly warns that it is developer-preview software and sandbox/approval controls are risk reducers, not a complete isolation boundary.
869
-
870
- Reference:
871
-
872
- - https://github.com/deepseek-ai/deepseek-harness/blob/master/SAFETY.md
873
-
874
- #### Read isolation caveat
875
-
876
- Current `read-only` means “no filesystem mutation,” not “the agent can only read its workspace.” Upstream discussion notes that read isolation outside the workspace is not currently guaranteed. Therefore a high-assurance QA deployment should avoid exposing generic shell/fs inspection tools and should mount only the data the QA runtime is intended to read.
877
-
878
- Reference:
879
-
880
- - https://github.com/deepseek-ai/deepseek-harness/discussions/492
881
-
882
- ---
883
-
884
- ## 12. Browser-side plugin activation
885
-
886
- ### 12.1 Dependencies
887
-
888
- The browser half should depend only on public DSH services required for:
889
-
890
- - slots;
891
- - client runtime/sessions;
892
- - connection state;
893
- - theme tokens;
894
- - settings/config projection;
895
- - optionally locale.
896
-
897
- Conceptual Cordis inject list:
898
-
899
- ```ts
900
- export const inject = [
901
- 'slots',
902
- 'sessions',
903
- 'connection',
904
- 'theme',
905
- // settings service used for qa-surface config
906
- ]
907
- ```
908
-
909
- Exact service names must be resolved against the target DSH release.
910
-
911
- ### 12.2 Slot registration
912
-
913
- Register one root-scoped entry into:
914
-
915
- ```text
916
- shell.overlay
917
- ```
918
-
919
- Conceptually:
920
-
921
- ```ts
922
- ctx.slots.inject('shell.overlay', () =>
923
- ctx.slots.register(
924
- {
925
- name: 'shell.overlay',
926
- key: 'dsh-qa-surface'
927
- },
928
- QaSurfaceEntry,
929
- ),
930
- )
931
- ```
932
-
933
- The entry remains mounted but returns `null` whenever the route is not active.
934
-
935
- Do not register into `root`, `sidebar`, or `conversation` as the default implementation.
936
-
937
- ---
938
-
939
- ## 13. Route handling
940
-
941
- Create a small `QaRouteController`.
942
-
943
- Responsibilities:
944
-
945
- - read `window.location.pathname`;
946
- - normalize route prefix;
947
- - publish `active: boolean`;
948
- - react to browser history changes;
949
- - react to `popstate`;
950
- - optionally wrap `pushState`/`replaceState` only if DSH provides no public navigation event;
951
- - restore patches on disposal.
952
-
953
- Matching rules for default `/qa`:
954
-
955
- ```text
956
- /qa → active
957
- /qa/ → active
958
- /qa/foo → active when matchChildren=true
959
- /qabc → inactive
960
- / → inactive
961
- /api/... → never intercepted
962
- ```
963
-
964
- ### 13.1 Important routing property
965
-
966
- Do not register a Host exact `/qa` route just to return HTML.
967
-
968
- DSH frontend-static already performs SPA fallback to `index.html` for unknown GET paths.
969
-
970
- A dedicated Host route is only justified later if `/qa` needs special HTTP headers or a different document.
971
-
972
- ---
973
-
974
- ## 14. Full-screen overlay behavior
975
-
976
- When QA route becomes active:
977
-
978
- 1. render a fixed full-viewport surface above AppFrame;
979
- 2. assign a high but locally-scoped z-index compatible with DSH overlays;
980
- 3. cover the full viewport including the native sidebar rail;
981
- 4. prevent background pointer interaction;
982
- 5. lock background scrolling;
983
- 6. ensure focus starts in the QA composer when appropriate;
984
- 7. restore all modified document state when route deactivates or plugin disposes.
985
-
986
- Recommended DOM marker:
987
-
988
- ```text
989
- body[data-dsh-qa-surface="active"]
990
- ```
991
-
992
- Use this marker for route-scoped global corrections only.
993
-
994
- Do not permanently patch DSH CSS.
995
-
996
- ### 14.1 Accessibility
997
-
998
- The hidden native UI below the overlay must not remain meaningfully keyboard-accessible.
999
-
1000
- Preferred order:
1001
-
1002
- 1. if a stable AppFrame root can be addressed through a public contract, set it inert while QA is active;
1003
- 2. otherwise implement focus containment inside the QA surface and prevent background pointer events;
1004
- 3. document any remaining limitation.
1005
-
1006
- Do not depend on brittle generated CSS class names to find native UI.
1007
-
1008
- ---
1009
-
1010
- ## 15. Session controller abstraction
1011
-
1012
- Create a plugin-owned adapter:
1013
-
1014
- ```text
1015
- QaSessionController
1016
- ```
1017
-
1018
- This is the only module allowed to know detailed DSH Session/Runtime method names.
1019
-
1020
- UI components consume a plugin-owned interface such as:
1021
-
1022
- ```ts
1023
- interface QaSessionState {
1024
- phase:
1025
- | 'idle'
1026
- | 'creating'
1027
- | 'ready'
1028
- | 'running'
1029
- | 'reconnecting'
1030
- | 'blocked'
1031
- | 'error'
1032
-
1033
- sessionId: string | null
1034
- messages: readonly QaMessage[]
1035
- error: string | null
1036
- canSend: boolean
1037
- canStop: boolean
1038
- }
1039
-
1040
- interface QaSessionActions {
1041
- ensureSession(): Promise<void>
1042
- send(text: string): Promise<void>
1043
- stop(): Promise<void>
1044
- reset(): Promise<void>
1045
- retryConnection(): void
1046
- }
1047
- ```
1048
-
1049
- Benefits:
1050
-
1051
- - isolates upstream DSH API changes;
1052
- - keeps React components DSH-version-agnostic;
1053
- - easy unit testing;
1054
- - avoids private component imports.
1055
-
1056
- ---
1057
-
1058
- ### 15.1 Optional chat-history sidebar
1059
-
1060
- `ui.showSessionList` (default `false`) renders a minimal sidebar beside the
1061
- conversation. Contract:
1062
-
1063
- - the index is per browser: localStorage `<storageKey>:v1:<route>:chats`,
1064
- session ids only, most recently used first, capped at 50;
1065
- - rows are the index intersected with the Host session list, so shared-host
1066
- users never see each other's chats;
1067
- - switching goes through the same bind + `secureSession` attestation path as
1068
- restore; a missing id is pruned and reported; an attestation failure keeps
1069
- the index entry and surfaces the generic configuration error;
1070
- - `New chat` in the sidebar follows the same `allowSessionReset` gate as the
1071
- header control; the previous chat stays in the index;
1072
- - each row carries a two-click delete control: the first click arms it, the
1073
- second removes the chat from this browser's index (the Host session stays —
1074
- DSH 0.1.x has no session-deletion seam); deleting the open chat continues
1075
- in a fresh attested session when resets are allowed;
1076
- - the sidebar hides below 600px viewports and under `fixed` policy it renders
1077
- without the new-chat control and never switches.
1078
-
1079
- ## 16. Session creation / restore algorithm
1080
-
1081
- ### 16.1 `browser-persistent`
1082
-
1083
- Pseudo-flow:
1084
-
1085
- ```text
1086
- activate /qa
1087
- │
1088
- ▼
1089
- load stored session id
1090
- │
1091
- ├── exists ──► resolve/open through DSH runtime
1092
- │ │
1093
- │ ├── valid ─► use it
1094
- │ └── gone/forbidden ─► clear local id
1095
- │
1096
- └───────────────────────────────────────┐
1097
- ▼
1098
- create DSH Session
1099
- │
1100
- ▼
1101
- apply preset/model
1102
- │
1103
- ▼
1104
- persist session id
1105
- │
1106
- ▼
1107
- ready
1108
- ```
1109
-
1110
- ### 16.2 Storage
1111
-
1112
- Use a versioned key:
1113
-
1114
- ```text
1115
- dsh-qa-surface:v1:<route>:session
1116
- ```
1117
-
1118
- Do not persist:
1119
-
1120
- - prompt text after send;
1121
- - raw credentials;
1122
- - provider secrets;
1123
- - tool results;
1124
- - transcript copies.
1125
-
1126
- The DSH Session is the authoritative transcript store.
1127
-
1128
- ### 16.3 Reset
1129
-
1130
- In default locked-down mode:
1131
-
1132
- ```yaml
1133
- lockdown:
1134
- allowSessionReset: false
1135
- ui:
1136
- showReset: false
1137
- ```
1138
-
1139
- so the QA user gets one persistent conversation and cannot create arbitrary additional sessions from the surface.
1140
-
1141
- If an operator explicitly enables Reset, it means:
1142
-
1143
- - stop active generation if needed;
1144
- - create a fresh DSH Session using the configured workspace/preset;
1145
- - apply and verify the locked QA permission policy before enabling Send;
1146
- - switch controller binding to it;
1147
- - replace stored session id;
1148
- - do not delete the old DSH Session automatically.
1149
-
1150
- Old sessions can remain available to operators in normal DSH Web.
1151
-
1152
- ---
1153
-
1154
- ## 17. Prompt sending
1155
-
1156
- The QA surface needs **plain text prompt submission**, not the full DSH developer composer.
1157
-
1158
- Preferred implementation:
1159
-
1160
- - use the public Session/client runtime prompt method;
1161
- - use Host-authoritative acceptance;
1162
- - disable duplicate sends while the submit request is unresolved;
1163
- - rely on DSH session events/snapshots for resulting user/assistant messages;
1164
- - do not optimistically fabricate an assistant message.
1165
-
1166
- Conceptual flow:
1167
-
1168
- ```text
1169
- QaComposer
1170
- │ send(text)
1171
- ▼
1172
- QaSessionController
1173
- │
1174
- ▼
1175
- public DSH Session.prompt(...)
1176
- │
1177
- ▼
1178
- Host Session Controller
1179
- │
1180
- ▼
1181
- Agent Loop
1182
- │
1183
- ├── LLM
1184
- ├── tools
1185
- ├── MCP
1186
- └── subagents
1187
- │
1188
- ▼
1189
- Session events
1190
- │
1191
- ▼
1192
- Client Session snapshot
1193
- │
1194
- ▼
1195
- QaTranscriptAdapter
1196
- │
1197
- ▼
1198
- QaTranscript
1199
- ```
1200
-
1201
- ### 17.1 Slash commands
1202
-
1203
- MVP QA input must treat normal text as text.
1204
-
1205
- Do not expose the DSH slash-command menu.
1206
-
1207
- If the public `Session.prompt()` path bypasses slash-command adjudication, that is desirable for the QA surface.
1208
-
1209
- If the selected DSH API automatically interprets slash commands, document it and either:
1210
-
1211
- - escape/disable them for QA;
1212
- - or explicitly allow them through config.
1213
-
1214
- Default must favor predictable QA text behavior.
1215
-
1216
- ---
1217
-
1218
- ## 18. Transcript projection
1219
-
1220
- Create:
1221
-
1222
- ```text
1223
- QaTranscriptAdapter
1224
- ```
1225
-
1226
- Input:
1227
-
1228
- - public DSH Session conversation snapshot/event projection.
1229
-
1230
- Output:
1231
-
1232
- ```ts
1233
- type QaMessage =
1234
- | {
1235
- id: string
1236
- role: 'user'
1237
- text: string
1238
- status: 'committed'
1239
- }
1240
- | {
1241
- id: string
1242
- role: 'assistant'
1243
- text: string
1244
- status: 'streaming' | 'committed' | 'failed'
1245
- }
1246
- | {
1247
- id: string
1248
- role: 'system'
1249
- text: string
1250
- status: 'info' | 'error'
1251
- }
1252
- ```
1253
-
1254
- ### 18.1 Visible by default
1255
-
1256
- Render:
1257
-
1258
- - user text;
1259
- - assistant visible text;
1260
- - generation failure summary;
1261
- - plugin-owned status notices.
1262
-
1263
- ### 18.2 Hidden by default
1264
-
1265
- Unless the corresponding operator-controlled work-detail option is enabled, do
1266
- not render:
1267
-
1268
- - reasoning chunks;
1269
- - tool call arguments;
1270
- - tool results;
1271
- - execution trajectory;
1272
- - subagent internals;
1273
- - system prompt;
1274
- - hidden metadata;
1275
- - raw event JSON;
1276
- - token usage.
1277
-
1278
- The events may still exist in the Session and normal DSH operator UI.
1279
-
1280
- ### 18.3 Tool activity
1281
-
1282
- Optional config:
1283
-
1284
- ```yaml
1285
- ui:
1286
- showToolActivity: true
1287
- showReasoning: true
1288
- ```
1289
-
1290
- projects one work disclosure for each DSH turn. While the turn is running the
1291
- disclosure is expanded and streams available reasoning/tool state. Once the
1292
- turn completes it collapses before the final answer to a duration summary such
1293
- as:
1294
-
1295
- ```text
1296
- Worked for 1m 24s
1297
- ```
1298
-
1299
- Expanding it shows reasoning, intermediate assistant progress, and tool rows.
1300
- Tool rows may independently reveal their formatted arguments and results.
1301
- These flags are presentation-only: they must not add tools, weaken
1302
- `lockdown.toolPolicy`, or expose system/context events and raw Host failures.
1303
-
1304
- ---
1305
-
1306
- ## 19. Streaming
1307
-
1308
- Assistant text should update from the same Session client snapshot used by normal DSH.
1309
-
1310
- Requirements:
1311
-
1312
- - do not create a second SSE/WebSocket connection;
1313
- - do not parse provider streams directly;
1314
- - use existing DSH connection generation/reconnect path;
1315
- - preserve partial assistant text while running;
1316
- - on reconnect, converge to Host/session history;
1317
- - no duplicated chunks after reconnect;
1318
- - scrolling should follow the latest assistant output only while the user remains near the bottom.
1319
-
1320
- ---
1321
-
1322
- ## 20. Composer behavior
1323
-
1324
- ### Default UX
1325
-
1326
- - multiline textarea;
1327
- - Enter → send;
1328
- - Shift+Enter → newline;
1329
- - disable Send for empty/whitespace-only draft;
1330
- - while running, show Stop;
1331
- - optionally allow another prompt only after current turn finishes in MVP;
1332
- - restore draft on transport error when submission was not accepted;
1333
- - clear draft only after Host accepts the prompt.
1334
-
1335
- ### Accessibility
1336
-
1337
- - textarea has explicit label/aria-label;
1338
- - Send and Stop are real buttons;
1339
- - busy status uses `aria-live` where appropriate;
1340
- - keyboard behavior documented;
1341
- - focus returns to composer after assistant completion unless the user moved focus intentionally.
1342
-
1343
- ---
1344
-
1345
- ## 21. Interactive tools, approvals and user questions
1346
-
1347
- This is a critical safety/UX area.
1348
-
1349
- DSH agents can enter states that require human interaction, for example:
1350
-
1351
- - permission approval;
1352
- - plan review;
1353
- - `ask_user_question` / interactive question.
1354
-
1355
- The QA surface must **not silently auto-approve** these states.
1356
-
1357
- ### MVP policy
1358
-
1359
- Default config:
1360
-
1361
- ```yaml
1362
- interaction:
1363
- approvals: blocked
1364
- questions: unsupported
1365
- ```
1366
-
1367
- Recommended deployment architecture:
1368
-
1369
- - force the QA session to the dedicated `qa-read-only` permission bundle;
1370
- - force `approval=never`;
1371
- - remove tools that require escalation rather than allowing an approval flow;
1372
- - use an allow-list of reviewed read-only tools;
1373
- - grant no mutable external-service permissions to the QA agent.
1374
-
1375
- In locked-down mode, an approval request is treated as a policy/configuration failure or an unsupported operation, not as something the QA user may approve.
1376
-
1377
- If the Session enters an unsupported pending interaction state:
1378
-
1379
- 1. stop accepting new text prompts;
1380
- 2. display a generic message such as:
1381
-
1382
- ```text
1383
- This request requires an interaction that is not available in this assistant view.
1384
- ```
1385
-
1386
- 3. provide Reset or Retry where sensible;
1387
- 4. keep the detailed interaction visible to an operator in normal DSH Web;
1388
- 5. never resolve the approval automatically.
1389
-
1390
- ### Post-MVP
1391
-
1392
- Add dedicated compact UI for:
1393
-
1394
- - questions/options;
1395
- - explicit allow/deny approval;
1396
- - plan review.
1397
-
1398
- These should use the public DSH interaction APIs, not DOM automation against native dialogs.
1399
-
1400
- ---
1401
-
1402
- ## 22. Connection and reconnect states
1403
-
1404
- Use DSH's existing browser-host connection.
1405
-
1406
- QA UI states:
1407
-
1408
- ### Initial connecting
1409
-
1410
- ```text
1411
- Connecting…
1412
- ```
1413
-
1414
- ### Reconnecting
1415
-
1416
- Keep transcript on screen and show a non-blocking banner:
1417
-
1418
- ```text
1419
- Connection lost. Reconnecting…
1420
- ```
1421
-
1422
- ### Restored
1423
-
1424
- Remove banner after Host state converges.
1425
-
1426
- ### Fatal connection error
1427
-
1428
- Show:
1429
-
1430
- - concise error;
1431
- - Retry button if the DSH connection layer supports it;
1432
- - no raw stack trace to QA user.
1433
-
1434
- Raw errors should still be logged for operators/development.
1435
-
1436
- ---
1437
-
1438
- ## 23. Error handling
1439
-
1440
- Map internal failures to QA-safe UI errors.
1441
-
1442
- Examples:
1443
-
1444
- | Internal class | QA message |
1445
- |---|---|
1446
- | session creation rejected | "Unable to start a chat." |
1447
- | prompt rejected | "Your message could not be sent." |
1448
- | model route unavailable | "The assistant is temporarily unavailable." |
1449
- | connection lost | "Connection lost. Reconnecting…" |
1450
- | unsupported approval | "This request requires operator interaction." |
1451
- | malformed persisted session id | silently clear and create a new session |
1452
-
1453
- Never render:
1454
-
1455
- - local filesystem paths;
1456
- - credentials;
1457
- - stack traces;
1458
- - raw model/provider payloads;
1459
- - internal tool arguments.
1460
-
1461
- Add structured client logging behind a debug flag.
1462
-
1463
- ---
1464
-
1465
- ## 24. Styling
1466
-
1467
- Follow DSH Web styling conventions where practical:
1468
-
1469
- - CSS Modules;
1470
- - reuse DSH semantic theme variables/tokens;
1471
- - no Tailwind dependency;
1472
- - no global CSS framework;
1473
- - no hardcoded assumption that DSH runs dark-only or light-only.
1474
-
1475
- QA surface may define local layout variables such as:
1476
-
1477
- ```css
1478
- .qaSurface {
1479
- --qa-max-content-width: 900px;
1480
- }
1481
- ```
1482
-
1483
- Shared color/typography values should derive from DSH theme tokens when available.
1484
-
1485
- ### 24.1 Responsive requirements
1486
-
1487
- Desktop:
1488
-
1489
- - centered content column;
1490
- - configurable max width;
1491
- - sticky composer bottom.
1492
-
1493
- Mobile:
1494
-
1495
- - full width;
1496
- - safe-area padding;
1497
- - textarea remains visible above virtual keyboard as far as browser allows;
1498
- - buttons remain touch-sized;
1499
- - no horizontal scroll.
1500
-
1501
- ---
1502
-
1503
- ## 25. Theme behavior
1504
-
1505
- The plugin should automatically follow DSH's resolved theme.
1506
-
1507
- Do not independently implement another dark/light preference system in MVP.
1508
-
1509
- Optional future config may force a branded theme, but normal DSH semantic variables should remain the base.
1510
-
1511
- ---
1512
-
1513
- ## 26. Security model
1514
-
1515
- ### 26.1 Same-origin only by default
1516
-
1517
- Preferred deployment:
1518
-
1519
- ```text
1520
- https://assistant.example.com/qa
1521
- https://assistant.example.com/api/...
1522
- ```
1523
-
1524
- or a reverse proxy exposing DSH under the same public origin.
1525
-
1526
- Do not implement cross-origin API calls merely for the QA page.
1527
-
1528
- ### 26.2 Preserve DSH trust fence
1529
-
1530
- Do not:
1531
-
1532
- - forge Host/Origin;
1533
- - disable browser trust globally;
1534
- - add permissive CORS;
1535
- - bypass signed browser-session/auth checks.
1536
-
1537
- ### 26.3 Authentication is deployment-owned
1538
-
1539
- `dsh-qa-surface` is primarily a **surface plugin**, not a full identity provider.
1540
-
1541
- For remote/public deployment, place it behind the deployment's intended auth layer, for example:
1542
-
1543
- - an existing DSH Web auth plugin;
1544
- - reverse-proxy SSO/OIDC;
1545
- - another explicitly configured access gateway.
1546
-
1547
- The plugin must document that the DSH Host webserver itself is not a complete internet-facing authentication boundary.
1548
-
1549
- ### 26.4 Embedding
1550
-
1551
- If iframe embedding is later enabled:
1552
-
1553
- - require explicit operator opt-in;
1554
- - use CSP `frame-ancestors` at the reverse proxy or supported Host layer;
1555
- - do not blindly set `*`;
1556
- - preserve same-site/session-cookie requirements.
1557
-
1558
- ---
1559
-
1560
- ### 26.5 Capability-change endpoints
1561
-
1562
- The QA browser bundle must not intentionally call mutable control-plane APIs for:
1563
-
1564
- - settings writes;
1565
- - plugin enable/disable/config changes;
1566
- - agent preset authoring/deletion/default changes;
1567
- - permission preset switching;
1568
- - sandbox mode changes;
1569
- - approval policy changes;
1570
- - workspace rebinding;
1571
- - model/provider switching;
1572
- - arbitrary session deletion/rename/import.
1573
-
1574
- If a public read API is needed to verify the effective state, use it read-only and project only the minimum information required by the QA controller.
1575
-
1576
- ### 26.6 UI disabled state is defense-in-depth only
1577
-
1578
- For every forbidden control that might accidentally appear because of future DSH composition changes:
1579
-
1580
- - do not render it in the QA component;
1581
- - if inherited UI becomes reachable, make it inaccessible/inert;
1582
- - keep controller actions absent, not merely disabled;
1583
- - add E2E tests proving no selector/menu/command can alter the locked policy.
1584
-
1585
- The absence of a button is not considered enforcement.
1586
-
1587
- ---
1588
-
1589
- ## 27. Operator vs QA-user separation
1590
-
1591
- Desired deployment:
1592
-
1593
- ```text
1594
- / → operator/developer DSH Web
1595
- /qa → end-user QA surface
1596
- ```
1597
-
1598
- Both may use the same Host/runtime and the same persisted Sessions.
1599
-
1600
- A QA-created session should remain inspectable by an authorized operator in normal DSH Web.
1601
-
1602
- The plugin must not try to hide the Session from the Host/operator data model.
1603
-
1604
- ---
1605
-
1606
- ## 28. Session naming / discoverability
1607
-
1608
- QA sessions should be recognizable in normal DSH Web.
1609
-
1610
- Options, in preference order:
1611
-
1612
- 1. use an agent preset dedicated to QA;
1613
- 2. set a title prefix when the public Session rename/title API makes this safe;
1614
- 3. add plugin metadata only if DSH offers a public extension point.
1615
-
1616
- Suggested title:
1617
-
1618
- ```text
1619
- QA: <auto-generated title>
1620
- ```
1621
-
1622
- Do not mutate raw Session log files directly.
1623
-
1624
- ---
1625
-
1626
- ## 29. Configuration UI
1627
-
1628
- MVP does not require a custom rich settings page.
1629
-
1630
- Preferred progression:
1631
-
1632
- ### Phase A
1633
-
1634
- Config through `cordis.patch.yml` / plugin config.
1635
-
1636
- ### Phase B
1637
-
1638
- Register the `qa-surface` settings namespace so values are manageable through normal DSH settings mechanisms.
1639
-
1640
- ### Phase C
1641
-
1642
- Optionally contribute a settings card under the DSH Plugins settings section.
1643
-
1644
- Fields suitable for UI editing:
1645
-
1646
- - enabled;
1647
- - route path;
1648
- - title;
1649
- - subtitle;
1650
- - welcome message;
1651
- - placeholder;
1652
- - workspace;
1653
- - agent preset;
1654
- - provider/model;
1655
- - reset visibility;
1656
- - suggested questions;
1657
- - interaction policy.
1658
-
1659
- Do not expose secrets in this settings namespace.
1660
-
1661
- ---
1662
-
1663
- ## 30. Example composition
1664
-
1665
- Illustrative only; exact Loader syntax must match the deployment.
1666
-
1667
- ```yaml
1668
- - insert:
1669
- - id: qa-surface
1670
- name: dsh-qa-surface
1671
- config:
1672
- enabled: true
1673
- route:
1674
- path: /qa
1675
- matchChildren: true
1676
- branding:
1677
- title: Internal Assistant
1678
- welcomeMessage: Ask a question about the platform.
1679
- placeholder: Ask a question...
1680
- session:
1681
- policy: browser-persistent
1682
- workspaceId: company-knowledge
1683
- agentPreset: qa-assistant
1684
- provider: null
1685
- model: null
1686
- lockdown:
1687
- enabled: true
1688
- enforceFixedAgentPreset: true
1689
- enforceFixedWorkspace: true
1690
- enforceFixedModel: true
1691
- sandboxMode: read-only
1692
- approvalPolicy: never
1693
- permissionPreset: qa-read-only
1694
- allowPermissionChanges: false
1695
- allowSlashCommands: false
1696
- allowSettingsMutation: false
1697
- allowSessionReset: false
1698
- allowArbitrarySessionOpen: false
1699
- toolPolicy:
1700
- mode: allow-list
1701
- allow:
1702
- - <reviewed-read-only-tool>
1703
- ui:
1704
- showReset: false
1705
- showStop: true
1706
- showToolActivity: false
1707
- showReasoning: false
1708
- ```
1709
-
1710
- If DSH patch semantics replace a row's entire config rather than merge fields, example docs must explicitly warn users to provide the complete config object required by that row.
1711
-
1712
- ---
1713
-
1714
- ## 31. Agent preset example
1715
-
1716
- The UI plugin should not own the assistant's domain prompt.
1717
-
1718
- Prefer a DSH agent preset conceptually like:
1719
-
1720
- ```text
1721
- qa-assistant
1722
- ├── system prompt / persona
1723
- ├── reviewed read-only knowledge/search tools only
1724
- ├── read-only Jira/Confluence MCP actions only, if needed
1725
- ├── selected skills
1726
- ├── no shell / terminal / generic code runtime by default
1727
- ├── no mutating external-service actions
1728
- ├── qa-read-only permission policy
1729
- └── fixed model policy, if deployment requires it
1730
- ```
1731
-
1732
- This makes the same assistant reusable from:
1733
-
1734
- - `/qa`;
1735
- - normal DSH Web;
1736
- - automation;
1737
- - future APIs.
1738
-
1739
- ---
1740
-
1741
- ## 32. Browser route lifecycle
1742
-
1743
- Expected transitions:
1744
-
1745
- ### `/` → `/qa`
1746
-
1747
- 1. route store becomes active;
1748
- 2. QA overlay mounts;
1749
- 3. background interaction is disabled;
1750
- 4. config loads;
1751
- 5. session is restored/created;
1752
- 6. transcript loads;
1753
- 7. composer becomes available.
1754
-
1755
- ### `/qa` → `/`
1756
-
1757
- 1. QA overlay unmounts/returns null;
1758
- 2. document scroll/focus modifications are restored;
1759
- 3. background DSH UI becomes interactive again;
1760
- 4. do not destroy the DSH Session;
1761
- 5. do not dispose global DSH runtime services.
1762
-
1763
- ### Browser reload on `/qa`
1764
-
1765
- 1. frontend-static returns index;
1766
- 2. normal DSH boot occurs;
1767
- 3. plugin activates;
1768
- 4. persisted QA Session is restored.
1769
-
1770
- ---
1771
-
1772
- ## 33. Native DSH UI interaction underneath overlay
1773
-
1774
- Because the normal DSH app remains mounted, the plugin must avoid accidental duplicate user interaction.
1775
-
1776
- Required:
1777
-
1778
- - overlay captures pointer events;
1779
- - native UI not visible;
1780
- - focus cannot casually tab into background;
1781
- - document shortcuts from native DSH should not unexpectedly trigger while typing in QA.
1782
-
1783
- If DSH exposes no public API to suspend global keyboard handlers, investigate whether:
1784
-
1785
- - stopping propagation from the QA surface is sufficient;
1786
- - AppFrame can be made `inert` through a stable public/semantic selector;
1787
- - a minimal route-scoped document guard is needed.
1788
-
1789
- Do not bind to minified/generated class names.
1790
-
1791
- ---
1792
-
1793
- ## 34. Observability
1794
-
1795
- The plugin should remain observable through the existing DSH/OTel stack where possible.
1796
-
1797
- Add lightweight plugin metrics/events only if DSH exposes a suitable seam.
1798
-
1799
- Useful dimensions:
1800
-
1801
- - QA surface opened;
1802
- - session created/restored;
1803
- - prompt accepted/rejected;
1804
- - turn completed/failed;
1805
- - reconnect;
1806
- - unsupported interaction encountered.
1807
-
1808
- Do not record prompt/message content in telemetry by default.
1809
-
1810
- Do not duplicate token/model telemetry already emitted by DSH.
1811
-
1812
- ---
1813
-
1814
- ## 35. Compatibility strategy
1815
-
1816
- DSH currently warns that it is in developer preview and may make breaking changes.
1817
-
1818
- The plugin must isolate DSH coupling.
1819
-
1820
- ### Adapter boundaries
1821
-
1822
- Keep these modules small:
1823
-
1824
- ```text
1825
- QaSessionController.ts DSH session runtime coupling
1826
- QaTranscriptAdapter.ts DSH conversation projection coupling
1827
- QaConfigController.ts DSH settings coupling
1828
- QaRouteController.ts browser navigation coupling
1829
- ```
1830
-
1831
- UI components should depend on plugin-local types only.
1832
-
1833
- ### Version support
1834
-
1835
- In README maintain a matrix:
1836
-
1837
- ```text
1838
- Plugin version Tested DSH versions
1839
- 0.1.x ...
1840
- 0.2.x ...
1841
- ```
1842
-
1843
- At startup, fail gracefully when a required client service/slot is absent.
1844
-
1845
- Do not silently mutate DSH internals to "repair" an unsupported version.
1846
-
1847
- ---
1848
-
1849
- ## 36. External client bundle compatibility
1850
-
1851
- An external DSH browser plugin must produce the module format expected by the DSH Web client module loader.
1852
-
1853
- The implementation agent must verify the current DSH external plugin build pattern before finalizing `tsdown.config.ts`.
1854
-
1855
- Requirements:
1856
-
1857
- - `./client` export resolves;
1858
- - package declares `dsh.client`;
1859
- - React/ReactDOM use DSH's shared runtime instances rather than bundling incompatible duplicates;
1860
- - client plugin bundle passes DSH's module/bundle purity expectations;
1861
- - CSS Modules are included correctly;
1862
- - source map optional but recommended;
1863
- - installation does not require rebuilding DSH's main frontend.
1864
-
1865
- Use an existing external browser UI plugin as a packaging reference if necessary, but keep runtime integration based on official DSH contracts.
1866
-
1867
- ---
1868
-
1869
- ## 37. Testing plan
1870
-
1871
- ### 37.1 Unit tests
1872
-
1873
- #### Config
1874
-
1875
- - default config validates;
1876
- - route normalization;
1877
- - invalid `/api` route rejected;
1878
- - invalid session policy rejected;
1879
- - fixed policy requires session id;
1880
- - empty title/placeholder behavior defined;
1881
- - lockdown defaults to enabled;
1882
- - locked-down default disables session reset;
1883
- - `qa-read-only` selection is required before first prompt when lockdown is enabled.
1884
-
1885
- #### Route controller
1886
-
1887
- - `/qa` activates;
1888
- - `/qa/` activates;
1889
- - `/qa/foo` activates when configured;
1890
- - `/qabc` does not activate;
1891
- - popstate updates state;
1892
- - disposal restores any wrapped history functions/listeners.
1893
-
1894
- #### Session controller
1895
-
1896
- - restore valid session;
1897
- - missing session creates new;
1898
- - forbidden/stale stored id clears storage;
1899
- - reset creates new id;
1900
- - send rejects empty string;
1901
- - accepted send clears draft at correct time;
1902
- - rejected send preserves draft;
1903
- - stop calls public DSH cancellation path;
1904
- - unsupported pending interaction blocks composer;
1905
- - agent/workspace/model mismatch blocks composer;
1906
- - non-`read-only` sandbox state blocks composer;
1907
- - approval policy other than `never` blocks composer;
1908
- - lockdown config cannot be weakened through browser-local state.
1909
-
1910
- #### Transcript adapter
1911
-
1912
- - user event → user QA message;
1913
- - assistant chunks coalesce correctly;
1914
- - reconnect replay does not duplicate text;
1915
- - tool calls excluded;
1916
- - reasoning excluded;
1917
- - assistant failure projected safely.
1918
-
1919
- ### 37.2 Component tests
1920
-
1921
- - composer keyboard rules;
1922
- - Stop replaces Send while running;
1923
- - reset confirmation if desired;
1924
- - reconnect banner;
1925
- - Markdown rendering;
1926
- - scroll-follow behavior;
1927
- - accessibility labels.
1928
-
1929
- ### 37.3 DSH integration tests
1930
-
1931
- Boot actual DSH Web with plugin mounted.
1932
-
1933
- Verify:
1934
-
1935
- 1. `/` still shows normal DSH UI;
1936
- 2. `/qa` returns 200 through SPA fallback;
1937
- 3. plugin client bundle appears in DSH client module graph;
1938
- 4. `/qa` renders full-screen QA UI;
1939
- 5. sending text creates/uses a real DSH Session;
1940
- 6. assistant streaming works;
1941
- 7. tool execution can occur without tool details becoming visible;
1942
- 8. reload restores the same session under browser-persistent policy;
1943
- 9. normal DSH UI can inspect the QA-created session;
1944
- 10. `/qa` does not open a second server/connection stack;
1945
- 11. every QA session starts with the configured agent preset;
1946
- 12. every QA session starts/effectively runs as `read-only`;
1947
- 13. approval policy is `never`;
1948
- 14. model/workspace selectors are absent;
1949
- 15. permission selectors/commands are absent;
1950
- 16. a denied write attempt cannot escalate;
1951
- 17. a newly installed global tool not present in the allow-list is not visible to the QA agent.
1952
-
1953
- ### 37.4 Security / lockdown tests
1954
-
1955
- - route does not weaken `/api` trust checks;
1956
- - config endpoint/settings do not leak secrets;
1957
- - raw tool result not visible;
1958
- - stack traces not visible;
1959
- - unsupported approval not auto-approved;
1960
- - `approval=never` rejects escalation deterministically;
1961
- - sandbox effective mode remains `read-only`;
1962
- - QA user cannot invoke `/permission` or equivalent command dispatch;
1963
- - QA user cannot change agent preset, workspace, provider, model, or reasoning mode;
1964
- - QA user cannot mutate DSH settings/plugin config;
1965
- - QA user cannot reset/start additional sessions when `allowSessionReset=false`;
1966
- - QA user cannot open arbitrary existing sessions when `allowArbitrarySessionOpen=false`;
1967
- - write/edit/delete filesystem tools are absent or denied;
1968
- - shell/terminal/code-runtime are absent by default;
1969
- - mutable MCP actions are absent;
1970
- - allow-listed MCP read actions still work;
1971
- - adding a new global write-capable tool does not implicitly expose it to QA;
1972
- - a preset-local tool is reviewed separately because scoped registrations may bypass inherited `ToolRestriction`;
1973
- - direct malformed/browser-local attempts to weaken lockdown fail closed;
1974
- - cross-origin behavior remains blocked unless deployment explicitly changes it.
1975
-
1976
- ### 37.5 Side-effect regression suite
1977
-
1978
- Maintain a table of every tool visible to `qa-assistant` and classify it:
1979
-
1980
- ```text
1981
- read-only / pure
1982
- external-read
1983
- filesystem-write
1984
- external-write
1985
- privilege/control-plane
1986
- unknown
1987
- ```
1988
-
1989
- CI must fail when a visible QA tool is `filesystem-write`, `external-write`, `privilege/control-plane`, or `unknown` unless the spec/config is intentionally updated and reviewed.
1990
-
1991
- This guards against future plugin installs silently expanding the QA agent's capability surface.
1992
-
1993
- ### 37.5 Mobile tests
1994
-
1995
- At minimum test:
1996
-
1997
- - 375px width;
1998
- - 430px width;
1999
- - landscape small viewport;
2000
- - virtual keyboard/composer visibility manually.
2001
-
2002
- ---
2003
-
2004
- ## 38. Acceptance criteria
2005
-
2006
- MVP is complete when all are true.
2007
-
2008
- ### Routing
2009
-
2010
- - [ ] `GET /qa` loads successfully using the normal DSH Web server.
2011
- - [ ] `/` remains unchanged.
2012
- - [ ] `/qa` shows no visible normal DSH navigation/settings UI.
2013
- - [ ] leaving `/qa` restores normal UI without reload when SPA navigation is used.
2014
-
2015
- ### Runtime
2016
-
2017
- - [ ] QA messages are sent through a real DSH Session.
2018
- - [ ] normal DSH tools/skills/MCP remain usable by the configured agent.
2019
- - [ ] no direct provider API call exists in the plugin.
2020
- - [ ] transcript persists through DSH.
2021
- - [ ] browser-persistent session survives reload.
2022
-
2023
- ### UI
2024
-
2025
- - [ ] streaming assistant text works.
2026
- - [ ] Send works.
2027
- - [ ] Stop works.
2028
- - [ ] Reset works.
2029
- - [ ] Markdown works.
2030
- - [ ] mobile layout is usable.
2031
- - [ ] tool calls/reasoning are hidden by default.
2032
-
2033
- ### Safety/security
2034
-
2035
- - [ ] no auto-approval.
2036
- - [ ] no raw internal errors in QA UI.
2037
- - [ ] no trust-fence bypass.
2038
- - [ ] no permissive CORS added.
2039
- - [ ] no secrets stored in localStorage.
2040
-
2041
- ### Maintainability
2042
-
2043
- - [ ] no DSH core patch required.
2044
- - [ ] no DSH frontend rebuild required for plugin installation.
2045
- - [ ] DSH-specific API coupling isolated behind adapters.
2046
- - [ ] compatibility matrix documented.
2047
-
2048
- ---
2049
-
2050
- ## 39. Implementation phases
2051
-
2052
- ### Phase 0 — Compatibility spike
2053
-
2054
- Goal: prove the external browser plugin packaging and public APIs before building UI.
2055
-
2056
- Tasks:
2057
-
2058
- 1. create package skeleton;
2059
- 2. build Host and `./client` halves;
2060
- 3. mount plugin through DSH config;
2061
- 4. register trivial `shell.overlay` entry;
2062
- 5. prove it appears only on `/qa`;
2063
- 6. inspect available public client runtime Session APIs;
2064
- 7. prove one session can be created/restored;
2065
- 8. prove one plain-text prompt can be sent;
2066
- 9. prove assistant text can be observed from a public snapshot;
2067
- 10. record exact tested DSH commit/version in `docs/COMPATIBILITY.md`.
2068
-
2069
- **Exit condition:** a hardcoded QA overlay can send `hello` through DSH and render the assistant reply without importing private components.
2070
-
2071
- ### Phase 1 — MVP surface
2072
-
2073
- Tasks:
2074
-
2075
- 1. route controller;
2076
- 2. full-screen shell.overlay surface;
2077
- 3. QA layout/components;
2078
- 4. session controller;
2079
- 5. transcript adapter;
2080
- 6. plain text composer;
2081
- 7. streaming;
2082
- 8. stop;
2083
- 9. reset;
2084
- 10. browser-persistent session id;
2085
- 11. errors/reconnect;
2086
- 12. Markdown;
2087
- 13. basic responsive CSS.
2088
-
2089
- ### Phase 2 — Configuration
2090
-
2091
- Tasks:
2092
-
2093
- 1. Schemastery config;
2094
- 2. settings namespace;
2095
- 3. browser config controller;
2096
- 4. branding values;
2097
- 5. workspace binding;
2098
- 6. preset/model selection;
2099
- 7. suggested questions;
2100
- 8. README config examples.
2101
-
2102
- ### Phase 3 — Hardening
2103
-
2104
- Tasks:
2105
-
2106
- 1. focus containment/background inert behavior;
2107
- 2. unsupported interaction handling;
2108
- 3. sanitization/error mapping;
2109
- 4. reconnect/replay tests;
2110
- 5. mobile fixes;
2111
- 6. accessibility pass;
2112
- 7. security test suite;
2113
- 8. compatibility guards.
2114
-
2115
- ### Phase 4 — Deployment/embedding
2116
-
2117
- Tasks:
2118
-
2119
- 1. Docker/local-plugin installation docs;
2120
- 2. reverse-proxy example;
2121
- 3. auth prerequisite docs;
2122
- 4. iframe notes;
2123
- 5. CSP/frame-ancestor guidance;
2124
- 6. production smoke test.
2125
-
2126
- ### Phase 5 — Optional native QA profile
2127
-
2128
- Only after plugin MVP is stable.
2129
-
2130
- Create a separate bundle/profile that composes:
2131
-
2132
- ```text
2133
- base runtime
2134
- + webserver
2135
- + connection/API
2136
- + client runtime
2137
- + theme
2138
- + qa surface
2139
- ```
2140
-
2141
- and omits unnecessary standard DSH UI packages.
2142
-
2143
- The existing `dsh-qa-surface` UI/controller code should be reusable without substantial rewrite.
2144
-
2145
- ---
2146
-
2147
- ## 40. Coding-agent instructions
2148
-
2149
- The implementation agent should follow these rules.
2150
-
2151
- 1. **Read current upstream source first.** DSH is moving quickly; verify all slot/service/method names against the installed version.
2152
- 2. **Do not deep-import private DSH React components.** If a needed behavior is not exposed publicly, implement a plugin-local presentation over the public object layer.
2153
- 3. **Do not patch DSH core for MVP.** Escalate instead if a true public extension point is missing.
2154
- 4. **Do not claim root/sidebar/conversation single slots globally.** MVP surface is additive `shell.overlay`.
2155
- 5. **Use the normal DSH Session Controller/client runtime.** No direct provider requests.
2156
- 6. **Treat Host state as authoritative.** Client-local state is UI/draft/session-id reference only.
2157
- 7. **Never auto-approve an interaction.** Unsupported interaction blocks safely.
2158
- 8. **Keep DSH-specific APIs behind adapter modules.**
2159
- 9. **Add tests together with each adapter.**
2160
- 10. **Record every upstream contract relied on in `docs/COMPATIBILITY.md`.**
2161
- 11. **Prefer same-origin.** Do not solve embedding by weakening Host/Origin checks.
2162
- 12. **Keep configuration declarative.** Support Docker/self-hosted deployment without manual post-update patching.
2163
-
2164
- ---
2165
-
2166
- ## 41. Suggested first implementation commits
2167
-
2168
- Recommended sequence:
2169
-
2170
- ```text
2171
- 1. chore: bootstrap dsh-qa-surface dual-face package
2172
- 2. feat: register route-aware shell.overlay entry
2173
- 3. feat: add qa full-screen layout and theme integration
2174
- 4. feat: add DSH session adapter and session restore/create
2175
- 5. feat: add transcript adapter and streaming assistant messages
2176
- 6. feat: add composer send/stop/reset flows
2177
- 7. feat: add plugin settings/config projection
2178
- 8. feat: add unsupported-interaction and reconnect states
2179
- 9. test: add DSH integration and route/session coverage
2180
- 10. docs: add install, configuration, security and compatibility docs
2181
- ```
2182
-
2183
- ---
2184
-
2185
- ## 42. Known risks
2186
-
2187
- ### Risk: upstream DSH client contracts change
2188
-
2189
- Mitigation:
2190
-
2191
- - adapter boundary;
2192
- - compatibility matrix;
2193
- - integration CI against pinned DSH version(s).
2194
-
2195
- ### Risk: `shell.overlay` is semantically a floating layer
2196
-
2197
- It is currently the safest additive frame-wide seat, but it is not a true global-page primitive.
2198
-
2199
- Mitigation:
2200
-
2201
- - treat it as MVP transport only;
2202
- - keep QA component independent of overlay mechanics;
2203
- - future migration to dedicated `qa` profile/global-page extension requires changing only the mount adapter.
2204
-
2205
- ### Risk: background DSH UI still exists
2206
-
2207
- Mitigation:
2208
-
2209
- - full viewport pointer capture;
2210
- - focus containment/inert where stable;
2211
- - route-scoped keyboard handling tests.
2212
-
2213
- ### Risk: hidden interactive approval blocks the agent
2214
-
2215
- Mitigation:
2216
-
2217
- - QA-specific permission preset;
2218
- - explicit pending-interaction detection;
2219
- - fail closed;
2220
- - later implement real compact interaction UI.
2221
-
2222
- ### Risk: multi-user use with browser-persistent sessions
2223
-
2224
- Browser-local session mapping is not an identity system.
2225
-
2226
- Mitigation:
2227
-
2228
- - MVP intended for authenticated/internal deployments;
2229
- - later add per-user mapping only when a stable authenticated user identity is available.
2230
-
2231
- ### Risk: workspace coupling
2232
-
2233
- QA agent may accidentally gain access to an overly broad workspace.
2234
-
2235
- Mitigation:
2236
-
2237
- - configure a dedicated QA workspace/preset;
2238
- - minimum permissions;
2239
- - do not automatically choose arbitrary workspaces.
2240
-
2241
- ---
2242
-
2243
- ### Risk: `read-only` is mistaken for “no side effects”
2244
-
2245
- DSH's read-only sandbox covers file mutation, not all process/network effects.
2246
-
2247
- Mitigation:
2248
-
2249
- - deny generic shell/terminal/code runtime by default;
2250
- - explicit tool allow-list;
2251
- - tool-by-tool MCP review;
2252
- - `approval=never`;
2253
- - optional network egress restrictions at container/proxy level.
2254
-
2255
- ### Risk: QA user can reach normal DSH Web
2256
-
2257
- If `/` and `/qa` share the same privileged identity, the user may bypass UI lockdown by opening the normal operator surface.
2258
-
2259
- Mitigation:
2260
-
2261
- - do not claim `/qa` alone is an authorization boundary;
2262
- - for untrusted users use separate auth roles/gateway or a dedicated QA DSH instance;
2263
- - preferably expose only the QA surface to the QA audience.
2264
-
2265
- ### Risk: global plugin installation expands tool surface
2266
-
2267
- Mitigation:
2268
-
2269
- - allow-list inherited tools;
2270
- - inspect preset-local registrations separately;
2271
- - CI capability inventory;
2272
- - fail closed on unknown tools where practical.
2273
-
2274
- ### Risk: read access is broader than intended
2275
-
2276
- Current DSH read-only mode does not imply workspace-only read isolation.
2277
-
2278
- Mitigation:
2279
-
2280
- - no generic shell/fs browsing in the default QA preset;
2281
- - dedicated read-only corpus/workspace;
2282
- - minimal container mounts;
2283
- - dedicated QA container for high-assurance deployments.
2284
-
2285
- ---
2286
-
2287
- ## 43. Future evolution
2288
-
2289
- Potential roadmap:
2290
-
2291
- ```text
2292
- 0.1 full-screen /qa overlay + one DSH session + hard lockdown
2293
- 0.2 settings + branding + suggested questions + capability inventory
2294
- 0.3 read-only sources/citations + optional supported questions UI
2295
- 0.4 attachments + feedback (without capability escalation)
2296
- 0.5 iframe/embed SDK + auth-role integration
2297
- 1.0 dedicated qa profile/bundle / isolated QA deployment option
2298
- ```
2299
-
2300
- Long term the project can become a reusable **DSH-powered assistant frontend** rather than merely an alternative theme.
2301
-
2302
- Possible future modes:
2303
-
2304
- ```text
2305
- /qa
2306
- /qa/<assistant-id>
2307
- /embed/qa/<assistant-id>
2308
- ```
2309
-
2310
- Each assistant id could map to a configured DSH agent preset, while the underlying runtime remains shared.
2311
-
2312
- ---
2313
-
2314
- ## 44. Research references
2315
-
2316
- Current architecture was checked against upstream DeepSeek Harness sources/docs on 2026-09-05.
2317
-
2318
- Key references:
2319
-
2320
- - DeepSeek Harness repository / developer-preview status
2321
- https://github.com/deepseek-ai/deepseek-harness
2322
-
2323
- - Web app bundle composition (`dsh-base` + browser roster)
2324
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/bundle/web-app/cordis.patch.yml
2325
-
2326
- - Web app bundle README
2327
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/bundle/web-app/README.md
2328
-
2329
- - Client modules / `dsh.client` / `window.__DSH_BOOT__`
2330
- https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/client-modules.md
2331
-
2332
- - Host webserver route/fallback model
2333
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/webserver/README.md
2334
-
2335
- - SPA frontend-static fallback
2336
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/frontend-static/README.md
2337
-
2338
- - Client runtime / Host-born sessions
2339
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/runtime/README.md
2340
-
2341
- - Session Controller
2342
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/README.md
2343
-
2344
- - Sandbox policy / `read-only` semantics
2345
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/sandbox/sandbox-policy/README.md
2346
-
2347
- - Approval policy / `never` fail-closed behavior
2348
- https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/approval.md
2349
-
2350
- - Permission presets (`sandbox` + `approval`)
2351
- https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/permission-presets.md
2352
-
2353
- - Tool restrictions / allow-list semantics
2354
- https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/tools.md
2355
-
2356
- - Agent preset session semantics
2357
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-agent-preset/README.md
2358
-
2359
- - DSH safety limitations
2360
- https://github.com/deepseek-ai/deepseek-harness/blob/master/SAFETY.md
2361
-
2362
- - Current read-isolation limitation discussion
2363
- https://github.com/deepseek-ai/deepseek-harness/discussions/492
2364
-
2365
-
2366
- - Web client slots
2367
- https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/slots
2368
-
2369
- - Conversation slot contract / `conversation.session`
2370
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-conversation/src/client/contract/slots.ts
2371
-
2372
- - UI layout shell / `shell.overlay`
2373
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-layout/README.md
2374
-
2375
- - Web client package rules / new UI plugin checklist
2376
- https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/AGENTS.md
2377
-
2378
- - Settings-card cookbook / external dual-face plugin pattern
2379
- https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cookbook/adding-a-settings-card.md
2380
-
2381
- ### Upstream limitation worth tracking
2382
-
2383
- At the time of writing there is no generic first-class global-page extension contract in the normal DSH shell; community discussion exists around this missing surface. The plugin should therefore keep its QA UI mount mechanism replaceable so it can migrate from `shell.overlay` if DSH later adds a proper global-page/work-area API.
2384
-
2385
- ---
2386
-
2387
- ## 45. Final architecture summary
2388
-
2389
- ```text
2390
- ┌──────────────────────────────┐
2391
- │ DeepSeek Harness │
2392
- │ │
2393
- │ Agent / Session / Tools │
2394
- │ Skills / MCP / Models │
2395
- │ Memory / Persistence / OTel │
2396
- └──────────────┬───────────────┘
2397
- │
2398
- Session Controller
2399
- │
2400
- API / connection
2401
- │
2402
- ┌──────────────────────┴──────────────────────┐
2403
- │ │
2404
- Normal DSH Web /qa route
2405
- │ │
2406
- standard AppFrame dsh-qa-surface client
2407
- │ │
2408
- sidebar/conversation shell.overlay (full)
2409
- settings/tools/etc. │
2410
- QaSessionController
2411
- │
2412
- QaTranscriptAdapter
2413
- │
2414
- QA React UI
2415
- │
2416
- textarea / Send
2417
- ```
2418
-
2419
- The core architectural principles are:
2420
-
2421
- > **Replace the presentation, not the harness.**
2422
-
2423
- > **Remove capabilities at the runtime/tool-policy layer; hiding buttons is only defense in depth.**
2424
-
2425
- `dsh-qa-surface` should be a thin QA/browser surface over the existing DSH runtime, with `/qa` acting as a dedicated end-user presentation while `/` remains the full operator/developer interface. In locked-down deployments, QA sessions must be bound to a fixed composition and a fail-closed read-only capability set.