@yadsh/dsh-qa-surface 0.3.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.
- package/README.md +153 -12
- package/lib/account-remotes.js +100 -0
- package/lib/account-remotes.js.map +1 -0
- package/lib/accounts/credentials.js +27 -0
- package/lib/accounts/credentials.js.map +1 -0
- package/lib/accounts/errors.js +9 -0
- package/lib/accounts/errors.js.map +1 -0
- package/lib/accounts/file.js +101 -0
- package/lib/accounts/file.js.map +1 -0
- package/lib/accounts/store.js +144 -161
- package/lib/accounts/store.js.map +1 -1
- package/lib/accounts/token.js +61 -0
- package/lib/accounts/token.js.map +1 -0
- package/lib/attachment-rules.js +111 -0
- package/lib/attachment-rules.js.map +1 -0
- package/lib/cli.js +122 -9
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +32 -2
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaSessionController.js +47 -65
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +121 -107
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +71 -6
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/attachments.js +150 -0
- package/lib/client/attachments.js.map +1 -0
- package/lib/client/attestation.js +1 -1
- package/lib/client/attestation.js.map +1 -1
- package/lib/client/base64.js +16 -0
- package/lib/client/base64.js.map +1 -0
- package/lib/client/chat-files.js +48 -0
- package/lib/client/chat-files.js.map +1 -0
- package/lib/client/components/Markdown.js +1 -1
- package/lib/client/components/Markdown.js.map +1 -1
- package/lib/client/components/QaChangelog.js +38 -19
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaComposer.js +56 -74
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaFileAttachment.js +19 -0
- package/lib/client/components/QaFileAttachment.js.map +1 -0
- package/lib/client/components/QaFilesPanel.js +45 -0
- package/lib/client/components/QaFilesPanel.js.map +1 -0
- package/lib/client/components/QaHeader.js +34 -0
- package/lib/client/components/QaHeader.js.map +1 -0
- package/lib/client/components/QaMessage.js +17 -4
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaModal.js +31 -0
- package/lib/client/components/QaModal.js.map +1 -0
- package/lib/client/components/QaProfile.js +64 -0
- package/lib/client/components/QaProfile.js.map +1 -0
- package/lib/client/components/QaRightRail.js +15 -0
- package/lib/client/components/QaRightRail.js.map +1 -0
- package/lib/client/components/QaSidebar.js +6 -1
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/{QaSourcesDrawer.js → QaSourcesPanel.js} +3 -3
- package/lib/client/components/QaSourcesPanel.js.map +1 -0
- package/lib/client/components/QaTurnRail.js +15 -0
- package/lib/client/components/QaTurnRail.js.map +1 -1
- package/lib/client/components/QaWidthHandle.js +25 -20
- package/lib/client/components/QaWidthHandle.js.map +1 -1
- package/lib/client/components/QaWorkGroup.js +19 -20
- package/lib/client/components/QaWorkGroup.js.map +1 -1
- package/lib/client/components/surface-utils.js +28 -0
- package/lib/client/components/surface-utils.js.map +1 -0
- package/lib/client/components/thinking-phrases.js +24 -0
- package/lib/client/components/thinking-phrases.js.map +1 -0
- package/lib/client/create-session.js +7 -32
- package/lib/client/create-session.js.map +1 -1
- package/lib/client/failure-copy.js +63 -0
- package/lib/client/failure-copy.js.map +1 -0
- package/lib/client/index.js +38 -7
- package/lib/client/index.js.map +1 -1
- package/lib/client/prompt-content.js +54 -0
- package/lib/client/prompt-content.js.map +1 -0
- package/lib/client/settings/card.js +143 -0
- package/lib/client/settings/card.js.map +1 -0
- package/lib/client/settings/fields.js +145 -0
- package/lib/client/settings/fields.js.map +1 -0
- package/lib/client/settings/format.js +255 -0
- package/lib/client/settings/format.js.map +1 -0
- package/lib/client/settings/sections/access.js +25 -0
- package/lib/client/settings/sections/access.js.map +1 -0
- package/lib/client/settings/sections/accounts.js +47 -0
- package/lib/client/settings/sections/accounts.js.map +1 -0
- package/lib/client/settings/sections/attachments.js +46 -0
- package/lib/client/settings/sections/attachments.js.map +1 -0
- package/lib/client/settings/sections/branding.js +28 -0
- package/lib/client/settings/sections/branding.js.map +1 -0
- package/lib/client/settings/sections/common.js +20 -0
- package/lib/client/settings/sections/common.js.map +1 -0
- package/lib/client/settings/sections/embedding.js +19 -0
- package/lib/client/settings/sections/embedding.js.map +1 -0
- package/lib/client/settings/sections/interface.js +60 -0
- package/lib/client/settings/sections/interface.js.map +1 -0
- package/lib/client/settings/sections/lockdown.js +82 -0
- package/lib/client/settings/sections/lockdown.js.map +1 -0
- package/lib/client/settings/sections/session.js +53 -0
- package/lib/client/settings/sections/session.js.map +1 -0
- package/lib/client/settings/sections/sources.js +72 -0
- package/lib/client/settings/sections/sources.js.map +1 -0
- package/lib/client/settings/sections/status.js +37 -0
- package/lib/client/settings/sections/status.js.map +1 -0
- package/lib/client/settings/sections.js +23 -0
- package/lib/client/settings/sections.js.map +1 -0
- package/lib/client/settings/styles.js +57 -0
- package/lib/client/settings/styles.js.map +1 -0
- package/lib/client/stream-publisher.js +47 -0
- package/lib/client/stream-publisher.js.map +1 -0
- package/lib/client/styles.js +85 -19
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js.map +1 -1
- package/lib/client/use-right-rail.js +96 -0
- package/lib/client/use-right-rail.js.map +1 -0
- package/lib/client/use-session-ui-state.js +26 -19
- package/lib/client/use-session-ui-state.js.map +1 -1
- package/lib/client.js +4788 -869
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/accounts.js +60 -0
- package/lib/config-resolvers/accounts.js.map +1 -0
- package/lib/config-resolvers/attachments.js +28 -0
- package/lib/config-resolvers/attachments.js.map +1 -0
- package/lib/config-resolvers/basics.js +59 -0
- package/lib/config-resolvers/basics.js.map +1 -0
- package/lib/config-resolvers/defaults.js +132 -0
- package/lib/config-resolvers/defaults.js.map +1 -0
- package/lib/config-resolvers/lockdown.js +79 -0
- package/lib/config-resolvers/lockdown.js.map +1 -0
- package/lib/config-resolvers/session.js +42 -0
- package/lib/config-resolvers/session.js.map +1 -0
- package/lib/config-resolvers/shared.js +42 -0
- package/lib/config-resolvers/shared.js.map +1 -0
- package/lib/config-resolvers/sources.js +81 -0
- package/lib/config-resolvers/sources.js.map +1 -0
- package/lib/config-resolvers/ui.js +20 -0
- package/lib/config-resolvers/ui.js.map +1 -0
- package/lib/config.js +65 -4
- package/lib/config.js.map +1 -1
- package/lib/entry-redirect.js +2 -1
- package/lib/entry-redirect.js.map +1 -1
- package/lib/index.js +123 -74
- package/lib/index.js.map +1 -1
- package/lib/profile.js +214 -0
- package/lib/profile.js.map +1 -0
- package/lib/prompt-notes.js +231 -0
- package/lib/prompt-notes.js.map +1 -0
- package/lib/resolve-config.js +35 -410
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +82 -16
- package/lib/secure-session.js.map +1 -1
- package/lib/shared/session-key.js +18 -0
- package/lib/shared/session-key.js.map +1 -0
- package/lib/shared/settings.js +10 -0
- package/lib/shared/settings.js.map +1 -0
- package/lib/thinking-phrases.js +68 -0
- package/lib/thinking-phrases.js.map +1 -0
- package/lib/typert.host.js +162 -17
- package/lib/typert.remote-client.d.ts +5 -1
- package/lib/typert.remote-client.js +132 -13
- package/lib/types/account-remotes.d.ts +42 -0
- package/lib/types/accounts/credentials.d.ts +14 -0
- package/lib/types/accounts/errors.d.ts +11 -0
- package/lib/types/accounts/file.d.ts +82 -0
- package/lib/types/accounts/store.d.ts +66 -43
- package/lib/types/accounts/token.d.ts +31 -0
- package/lib/types/attachment-rules.d.ts +44 -0
- package/lib/types/cli.d.ts +2 -2
- package/lib/types/client/QaAccountsController.d.ts +8 -1
- package/lib/types/client/QaSessionController.d.ts +16 -16
- package/lib/types/client/QaSurface.d.ts +7 -1
- package/lib/types/client/attachments.d.ts +54 -0
- package/lib/types/client/base64.d.ts +10 -0
- package/lib/types/client/chat-files.d.ts +30 -0
- package/lib/types/client/components/QaChangelog.d.ts +1 -1
- package/lib/types/client/components/QaComposer.d.ts +8 -4
- package/lib/types/client/components/QaFileAttachment.d.ts +14 -0
- package/lib/types/client/components/QaFilesPanel.d.ts +15 -0
- package/lib/types/client/components/QaHeader.d.ts +41 -0
- package/lib/types/client/components/QaMessage.d.ts +3 -1
- package/lib/types/client/components/QaModal.d.ts +26 -0
- package/lib/types/client/components/QaProfile.d.ts +25 -0
- package/lib/types/client/components/QaRightRail.d.ts +23 -0
- package/lib/types/client/components/QaSidebar.d.ts +14 -0
- package/lib/types/client/components/{QaSourcesDrawer.d.ts → QaSourcesPanel.d.ts} +7 -4
- package/lib/types/client/components/QaTurnRail.d.ts +6 -0
- package/lib/types/client/components/QaWidthHandle.d.ts +14 -7
- package/lib/types/client/components/QaWorkGroup.d.ts +5 -3
- package/lib/types/client/components/surface-utils.d.ts +11 -0
- package/lib/types/client/components/thinking-phrases.d.ts +9 -0
- package/lib/types/client/create-session.d.ts +6 -9
- package/lib/types/client/failure-copy.d.ts +19 -0
- package/lib/types/client/prompt-content.d.ts +30 -0
- package/lib/types/client/settings/card.d.ts +23 -0
- package/lib/types/client/settings/fields.d.ts +139 -0
- package/lib/types/client/settings/format.d.ts +108 -0
- package/lib/types/client/settings/sections/access.d.ts +9 -0
- package/lib/types/client/settings/sections/accounts.d.ts +9 -0
- package/lib/types/client/settings/sections/attachments.d.ts +15 -0
- package/lib/types/client/settings/sections/branding.d.ts +8 -0
- package/lib/types/client/settings/sections/common.d.ts +28 -0
- package/lib/types/client/settings/sections/embedding.d.ts +8 -0
- package/lib/types/client/settings/sections/interface.d.ts +9 -0
- package/lib/types/client/settings/sections/lockdown.d.ts +9 -0
- package/lib/types/client/settings/sections/session.d.ts +9 -0
- package/lib/types/client/settings/sections/sources.d.ts +9 -0
- package/lib/types/client/settings/sections/status.d.ts +22 -0
- package/lib/types/client/settings/sections.d.ts +25 -0
- package/lib/types/client/settings/styles.d.ts +8 -0
- package/lib/types/client/stream-publisher.d.ts +28 -0
- package/lib/types/client/types.d.ts +29 -1
- package/lib/types/client/use-right-rail.d.ts +56 -0
- package/lib/types/client/use-session-ui-state.d.ts +13 -7
- package/lib/types/config-resolvers/accounts.d.ts +15 -0
- package/lib/types/config-resolvers/attachments.d.ts +6 -0
- package/lib/types/config-resolvers/basics.d.ts +8 -0
- package/lib/types/config-resolvers/defaults.d.ts +4 -0
- package/lib/types/config-resolvers/lockdown.d.ts +10 -0
- package/lib/types/config-resolvers/session.d.ts +6 -0
- package/lib/types/config-resolvers/shared.d.ts +16 -0
- package/lib/types/config-resolvers/sources.d.ts +6 -0
- package/lib/types/config-resolvers/ui.d.ts +6 -0
- package/lib/types/index.d.ts +20 -13
- package/lib/types/profile.d.ts +76 -0
- package/lib/types/prompt-notes.d.ts +110 -0
- package/lib/types/resolve-config.d.ts +8 -3
- package/lib/types/secure-session.d.ts +26 -1
- package/lib/types/shared/session-key.d.ts +23 -0
- package/lib/types/shared/settings.d.ts +10 -0
- package/lib/types/thinking-phrases.d.ts +17 -0
- package/lib/types/types.d.ts +123 -5
- package/lib/types/user-workspace.d.ts +38 -0
- package/lib/user-workspace.js +231 -0
- package/lib/user-workspace.js.map +1 -0
- package/package.json +14 -7
- package/CHANGELOG.md +0 -176
- package/SPEC-dsh-qa-surface.md +0 -2425
- package/docs/ARCHITECTURE.md +0 -66
- package/docs/COMPATIBILITY.md +0 -14
- package/docs/CONFIGURATION.md +0 -199
- package/docs/EMBEDDING.md +0 -13
- package/docs/SOURCES-MIGRATION.md +0 -28
- package/docs/SOURCES-PROVENANCE-AUDIT.md +0 -88
- package/docs/SPEC-dsh-qa-surface-accounts.md +0 -219
- package/docs/SPEC-dsh-qa-surface-sources-provenance.md +0 -1692
- package/lib/client/components/QaSourcesDrawer.js.map +0 -1
package/SPEC-dsh-qa-surface.md
DELETED
|
@@ -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.
|