@yadsh/dsh-qa-surface 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -1
- package/README.md +87 -12
- package/compatibility.json +11 -3
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/COMPATIBILITY.md +1 -1
- package/docs/CONFIGURATION.md +88 -1
- package/docs/SOURCES-MIGRATION.md +28 -0
- package/docs/SOURCES-PROVENANCE-AUDIT.md +88 -0
- package/docs/SPEC-dsh-qa-surface-accounts.md +219 -0
- package/docs/SPEC-dsh-qa-surface-sources-provenance.md +1692 -0
- package/lib/accounts/index.js +2 -0
- package/lib/accounts/index.js.map +1 -0
- package/lib/accounts/store.js +478 -0
- package/lib/accounts/store.js.map +1 -0
- package/lib/attestation.js.map +1 -1
- package/lib/cli.js +143 -0
- package/lib/cli.js.map +1 -0
- package/lib/client/QaAccountsController.js +329 -0
- package/lib/client/QaAccountsController.js.map +1 -0
- package/lib/client/QaSessionController.js +203 -127
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +234 -72
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +27 -241
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/attestation.js +6 -0
- package/lib/client/attestation.js.map +1 -1
- package/lib/client/components/Markdown.js +54 -13
- package/lib/client/components/Markdown.js.map +1 -1
- package/lib/client/components/QaAuthGate.js +40 -0
- package/lib/client/components/QaAuthGate.js.map +1 -0
- package/lib/client/components/QaChangelog.js +112 -0
- package/lib/client/components/QaChangelog.js.map +1 -0
- package/lib/client/components/QaMessage.js +47 -17
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaSidebar.js +63 -20
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaSourcesDrawer.js +113 -32
- package/lib/client/components/QaSourcesDrawer.js.map +1 -1
- package/lib/client/components/QaTurnRail.js +281 -0
- package/lib/client/components/QaTurnRail.js.map +1 -0
- package/lib/client/components/QaWelcomeNotice.js +125 -0
- package/lib/client/components/QaWelcomeNotice.js.map +1 -0
- package/lib/client/components/QaWidthHandle.js +142 -0
- package/lib/client/components/QaWidthHandle.js.map +1 -0
- package/lib/client/components/source-refs.js +78 -0
- package/lib/client/components/source-refs.js.map +1 -0
- package/lib/client/create-session.js +51 -0
- package/lib/client/create-session.js.map +1 -0
- package/lib/client/index.js +100 -12
- package/lib/client/index.js.map +1 -1
- package/lib/client/project-session-state.js +78 -0
- package/lib/client/project-session-state.js.map +1 -0
- package/lib/client/session-admission.js +38 -0
- package/lib/client/session-admission.js.map +1 -0
- package/lib/client/session-assets.js +52 -0
- package/lib/client/session-assets.js.map +1 -0
- package/lib/client/session-sources.js +61 -0
- package/lib/client/session-sources.js.map +1 -0
- package/lib/client/settlement.js +24 -0
- package/lib/client/settlement.js.map +1 -0
- package/lib/client/styles.js +151 -9
- package/lib/client/styles.js.map +1 -1
- package/lib/client/tool-presentation.js +102 -0
- package/lib/client/tool-presentation.js.map +1 -0
- package/lib/client/turn-sources.js +98 -0
- package/lib/client/turn-sources.js.map +1 -0
- package/lib/client/types.js +2 -0
- package/lib/client/types.js.map +1 -1
- package/lib/client/use-session-ui-state.js +68 -0
- package/lib/client/use-session-ui-state.js.map +1 -0
- package/lib/client/use-transcript-view.js +44 -0
- package/lib/client/use-transcript-view.js.map +1 -0
- package/lib/client.js +4649 -861
- package/lib/client.js.map +1 -1
- package/lib/config.js +218 -102
- package/lib/config.js.map +1 -1
- package/lib/entry-redirect.js +44 -0
- package/lib/entry-redirect.js.map +1 -0
- package/lib/host-route.js +25 -1
- package/lib/host-route.js.map +1 -1
- package/lib/index.js +209 -23
- package/lib/index.js.map +1 -1
- package/lib/launch-token.js +42 -0
- package/lib/launch-token.js.map +1 -0
- package/lib/provenance/collector.js +40 -0
- package/lib/provenance/collector.js.map +1 -0
- package/lib/provenance/connectors.js +147 -0
- package/lib/provenance/connectors.js.map +1 -0
- package/lib/provenance/dedupe.js +89 -0
- package/lib/provenance/dedupe.js.map +1 -0
- package/lib/provenance/extractors.js +340 -0
- package/lib/provenance/extractors.js.map +1 -0
- package/lib/provenance/file-preview.js +39 -0
- package/lib/provenance/file-preview.js.map +1 -0
- package/lib/provenance/host-store.js +387 -0
- package/lib/provenance/host-store.js.map +1 -0
- package/lib/provenance/index.js +9 -0
- package/lib/provenance/index.js.map +1 -0
- package/lib/provenance/normalize.js +128 -0
- package/lib/provenance/normalize.js.map +1 -0
- package/lib/provenance/reported.js +53 -0
- package/lib/provenance/reported.js.map +1 -0
- package/lib/provenance/types.js +2 -0
- package/lib/provenance/types.js.map +1 -0
- package/lib/resolve-config.js +161 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +50 -8
- package/lib/secure-session.js.map +1 -1
- package/lib/typert.host.js +599 -6
- package/lib/typert.remote-client.d.ts +19 -3
- package/lib/typert.remote-client.js +471 -3
- package/lib/types/accounts/index.d.ts +2 -0
- package/lib/types/accounts/store.d.ts +121 -0
- package/lib/types/attestation.d.ts +1 -1
- package/lib/types/cli.d.ts +9 -0
- package/lib/types/client/QaAccountsController.d.ts +95 -0
- package/lib/types/client/QaConfigController.d.ts +1 -1
- package/lib/types/client/QaSessionController.d.ts +46 -7
- package/lib/types/client/QaSurface.d.ts +8 -3
- package/lib/types/client/QaTranscriptAdapter.d.ts +5 -10
- package/lib/types/client/components/Markdown.d.ts +11 -1
- package/lib/types/client/components/QaAgentsDrawer.d.ts +1 -1
- package/lib/types/client/components/QaAuthGate.d.ts +15 -0
- package/lib/types/client/components/QaChangelog.d.ts +21 -0
- package/lib/types/client/components/QaMessage.d.ts +10 -3
- package/lib/types/client/components/QaSidebar.d.ts +30 -2
- package/lib/types/client/components/QaSourcesDrawer.d.ts +15 -3
- package/lib/types/client/components/QaTurnRail.d.ts +59 -0
- package/lib/types/client/components/QaWelcomeNotice.d.ts +27 -0
- package/lib/types/client/components/QaWidthHandle.d.ts +36 -0
- package/lib/types/client/components/source-refs.d.ts +16 -0
- package/lib/types/client/create-session.d.ts +16 -0
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/project-session-state.d.ts +30 -0
- package/lib/types/client/session-admission.d.ts +23 -0
- package/lib/types/client/session-assets.d.ts +20 -0
- package/lib/types/client/session-sources.d.ts +28 -0
- package/lib/types/client/settlement.d.ts +6 -0
- package/lib/types/client/tool-presentation.d.ts +6 -0
- package/lib/types/client/turn-sources.d.ts +14 -0
- package/lib/types/client/types.d.ts +73 -9
- package/lib/types/client/use-session-ui-state.d.ts +35 -0
- package/lib/types/client/use-transcript-view.d.ts +20 -0
- package/lib/types/entry-redirect.d.ts +20 -0
- package/lib/types/host-route.d.ts +14 -1
- package/lib/types/index.d.ts +47 -3
- package/lib/types/launch-token.d.ts +21 -0
- package/lib/types/provenance/collector.d.ts +26 -0
- package/lib/types/provenance/connectors.d.ts +11 -0
- package/lib/types/provenance/dedupe.d.ts +6 -0
- package/lib/types/provenance/extractors.d.ts +14 -0
- package/lib/types/provenance/file-preview.d.ts +10 -0
- package/lib/types/provenance/host-store.d.ts +54 -0
- package/lib/types/provenance/index.d.ts +10 -0
- package/lib/types/provenance/normalize.d.ts +13 -0
- package/lib/types/provenance/reported.d.ts +5 -0
- package/lib/types/provenance/types.d.ts +78 -0
- package/lib/types/secure-session.d.ts +11 -2
- package/lib/types/types.d.ts +167 -11
- package/package.json +58 -41
|
@@ -0,0 +1,1692 @@
|
|
|
1
|
+
# SPEC: Structured Sources / Provenance for `dsh-qa-surface`
|
|
2
|
+
|
|
3
|
+
Status: Draft / implementation plan
|
|
4
|
+
Target: `plugins/dsh-qa-surface`
|
|
5
|
+
Scope: source collection, source inheritance from subagents, unified source UI, source preview, Markdown rendering for local `.md` files
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Goal
|
|
10
|
+
|
|
11
|
+
Доработать `dsh-qa-surface` так, чтобы источники ответа перестали быть свободно оформленным текстом, который LLM вручную дописывает в конец ответа, и стали **структурированными данными, собираемыми плагином автоматически**.
|
|
12
|
+
|
|
13
|
+
После доработки:
|
|
14
|
+
|
|
15
|
+
- главный агент не обязан вручную формировать раздел `Источники`;
|
|
16
|
+
- источники собираются из фактических tool calls/results текущего turn;
|
|
17
|
+
- источники субагентов автоматически наследуются родительским turn;
|
|
18
|
+
- один и тот же структурированный набор источников используется:
|
|
19
|
+
- в правой панели Sources;
|
|
20
|
+
- в компактном footer под ответом;
|
|
21
|
+
- при необходимости — для inline citations;
|
|
22
|
+
- одинаковые источники дедуплицируются;
|
|
23
|
+
- web search discovery не раздувает список до десятков/сотен малоценных ссылок;
|
|
24
|
+
- локальные файловые источники можно открыть прямо из QA UI;
|
|
25
|
+
- `.md` / `.markdown` файлы по умолчанию открываются как отформатированный Markdown, а не как plain text;
|
|
26
|
+
- у Markdown preview есть переключение `Rendered / Raw`;
|
|
27
|
+
- источник сохраняет происхождение: parent agent / subagent / конкретный tool call / turn.
|
|
28
|
+
|
|
29
|
+
Главный принцип:
|
|
30
|
+
|
|
31
|
+
> **LLM отвечает за содержание ответа. Плагин отвечает за provenance и отображение источников.**
|
|
32
|
+
|
|
33
|
+
Не использовать natural-language блок `Источники` в assistant message как canonical source of truth.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Current problems
|
|
38
|
+
|
|
39
|
+
По текущему UX наблюдаются несколько проблем.
|
|
40
|
+
|
|
41
|
+
### 2.1. Источники в ответе нестабильны
|
|
42
|
+
|
|
43
|
+
LLM может оформить их как:
|
|
44
|
+
|
|
45
|
+
```md
|
|
46
|
+
## Источники
|
|
47
|
+
|
|
48
|
+
1. Документация ...
|
|
49
|
+
2. Jira ...
|
|
50
|
+
3. Confluence ...
|
|
51
|
+
4. Код ...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
или в другом порядке/формате, объединить несколько ссылок в одну строку, забыть часть источников, добавить текст без URL, поменять названия секций и т.п.
|
|
55
|
+
|
|
56
|
+
Это делает формат зависимым от модели и prompt adherence.
|
|
57
|
+
|
|
58
|
+
### 2.2. Ответ и Sources sidebar расходятся
|
|
59
|
+
|
|
60
|
+
Источник может присутствовать в тексте ответа, но отсутствовать в боковом списке, или наоборот.
|
|
61
|
+
|
|
62
|
+
Причина архитектурная: две поверхности получают данные разными путями.
|
|
63
|
+
|
|
64
|
+
### 2.3. Список Sources слишком шумный
|
|
65
|
+
|
|
66
|
+
Текущий Activity/Sources может показывать десятки web результатов (`Web · 72`, `Sources · 100`). Для QA-пользователя это практически не отличается от лога поискового движка.
|
|
67
|
+
|
|
68
|
+
Особенно плохо считать каждый результат `web_search` полноценным использованным источником, если агент реально открыл/прочитал только несколько страниц.
|
|
69
|
+
|
|
70
|
+
### 2.4. Источники субагентов теряются
|
|
71
|
+
|
|
72
|
+
Parent agent видит финальный ответ child/subagent, но provenance внутренних tool calls ребёнка может не попадать в итоговый source list родительского turn.
|
|
73
|
+
|
|
74
|
+
### 2.5. File preview слишком сырой
|
|
75
|
+
|
|
76
|
+
Markdown-файлы сейчас могут открываться как raw/plain text. Для документации это заметно хуже стандартного rendered Markdown.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. Non-goals
|
|
81
|
+
|
|
82
|
+
В рамках этой доработки не требуется:
|
|
83
|
+
|
|
84
|
+
- строить полноценную citation-verification систему, доказывающую, что каждая фраза ответа логически подтверждается источником;
|
|
85
|
+
- автоматически генерировать библиографию в академическом стиле;
|
|
86
|
+
- делать editable file viewer;
|
|
87
|
+
- выполнять JavaScript/HTML из Markdown;
|
|
88
|
+
- давать QA-пользователю filesystem navigation;
|
|
89
|
+
- открывать произвольные файлы, которые агент не использовал как source;
|
|
90
|
+
- превращать каждый grep/glob/search hit в пользовательский источник;
|
|
91
|
+
- менять read-only/security policy `/qa`;
|
|
92
|
+
- форкать DSH core ради source tracking.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 4. Design principles
|
|
97
|
+
|
|
98
|
+
### 4.1. Structured first
|
|
99
|
+
|
|
100
|
+
Canonical representation источника — typed object, а не строка Markdown.
|
|
101
|
+
|
|
102
|
+
### 4.2. Observe actual tool usage
|
|
103
|
+
|
|
104
|
+
Плагин должен в первую очередь собирать provenance из tool presentation/result metadata и session events.
|
|
105
|
+
|
|
106
|
+
Не просить LLM повторно пересказывать то, что runtime уже знает.
|
|
107
|
+
|
|
108
|
+
### 4.3. Evidence != discovery
|
|
109
|
+
|
|
110
|
+
Нужно различать:
|
|
111
|
+
|
|
112
|
+
- **evidence / consumed source** — документ реально был прочитан/получен агентом;
|
|
113
|
+
- **discovery candidate** — ссылка/файл только встретился в поисковой выдаче;
|
|
114
|
+
- **navigation artifact** — glob/list/grep entry, использованный только для навигации.
|
|
115
|
+
|
|
116
|
+
Основной Sources UI показывает evidence sources.
|
|
117
|
+
|
|
118
|
+
Discovery candidates могут быть доступны в debug/activity view, но не должны автоматически раздувать пользовательский список Sources.
|
|
119
|
+
|
|
120
|
+
### 4.4. Same data for every presentation
|
|
121
|
+
|
|
122
|
+
Sources sidebar, footer и inline citation resolver должны получать один `QaTurnSources` snapshot.
|
|
123
|
+
|
|
124
|
+
### 4.5. Fail soft, not silently wrong
|
|
125
|
+
|
|
126
|
+
Если provenance конкретного внешнего subagent provider недоступен, UI должен уметь показать:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
Sources may be incomplete for 1 delegated run
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
в debug mode, а не притворяться, что список гарантированно полный.
|
|
133
|
+
|
|
134
|
+
### 4.6. Reuse DSH structured presentation metadata
|
|
135
|
+
|
|
136
|
+
DSH уже имеет structured tool presentation types, включая:
|
|
137
|
+
|
|
138
|
+
- file read result (`ReadResultView` с `path`, `offset`, numbered `lines`, `totalLines`, `lang`);
|
|
139
|
+
- file search result (`SearchResultView`);
|
|
140
|
+
- web search result (`WebSearchResultView.sources`);
|
|
141
|
+
- web fetch result (`WebFetchResultView.url`);
|
|
142
|
+
- generic call locations (`FileLocation`).
|
|
143
|
+
|
|
144
|
+
Где эти данные доступны, extractor обязан использовать их вместо regex parsing model-facing text.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. High-level architecture
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
Parent Agent
|
|
152
|
+
│
|
|
153
|
+
tool calls / tool results
|
|
154
|
+
│
|
|
155
|
+
▼
|
|
156
|
+
┌──────────────────┐
|
|
157
|
+
│ Source Collector │◄─────────────┐
|
|
158
|
+
└────────┬─────────┘ │
|
|
159
|
+
│ │
|
|
160
|
+
normalize / classify / dedupe │
|
|
161
|
+
│ │
|
|
162
|
+
│ Child/Subagent tools
|
|
163
|
+
│ │
|
|
164
|
+
│ ┌─────────┴─────────┐
|
|
165
|
+
│ │ Subagent lineage │
|
|
166
|
+
│ │ + child collector │
|
|
167
|
+
│ └─────────┬─────────┘
|
|
168
|
+
│ │
|
|
169
|
+
└────────────┬───────────┘
|
|
170
|
+
│
|
|
171
|
+
turn/end
|
|
172
|
+
│
|
|
173
|
+
▼
|
|
174
|
+
┌─────────────────────┐
|
|
175
|
+
│ qa/sources event │
|
|
176
|
+
│ QaTurnSources │
|
|
177
|
+
└──────────┬──────────┘
|
|
178
|
+
│
|
|
179
|
+
┌───────────────┼────────────────┐
|
|
180
|
+
│ │ │
|
|
181
|
+
▼ ▼ ▼
|
|
182
|
+
Sources sidebar Answer footer Inline citation
|
|
183
|
+
resolver
|
|
184
|
+
│
|
|
185
|
+
▼
|
|
186
|
+
Source preview
|
|
187
|
+
│
|
|
188
|
+
┌────────────┴────────────┐
|
|
189
|
+
│ │
|
|
190
|
+
Web/Jira/... Local file
|
|
191
|
+
│
|
|
192
|
+
┌──────────┴──────────┐
|
|
193
|
+
│ Markdown | Raw text │
|
|
194
|
+
└─────────────────────┘
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 6. Canonical data model
|
|
200
|
+
|
|
201
|
+
Suggested types:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
export type QaSourceKind =
|
|
205
|
+
"file" | "code" | "web" | "jira" | "confluence" | "knowledge" | "other";
|
|
206
|
+
|
|
207
|
+
export type QaSourceEvidence =
|
|
208
|
+
"read" | "fetched" | "queried" | "reported" | "inherited" | "discovered";
|
|
209
|
+
|
|
210
|
+
export interface QaSourceLocation {
|
|
211
|
+
path?: string;
|
|
212
|
+
lineStart?: number;
|
|
213
|
+
lineEnd?: number;
|
|
214
|
+
anchor?: string;
|
|
215
|
+
|
|
216
|
+
jiraKey?: string;
|
|
217
|
+
confluencePageId?: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface QaSourceOrigin {
|
|
221
|
+
sessionId: string;
|
|
222
|
+
turn: number;
|
|
223
|
+
step?: number;
|
|
224
|
+
toolCallId?: string;
|
|
225
|
+
toolName?: string;
|
|
226
|
+
|
|
227
|
+
agentId?: string;
|
|
228
|
+
role: "parent" | "subagent";
|
|
229
|
+
|
|
230
|
+
subagentRunId?: string;
|
|
231
|
+
subagentSessionId?: string;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface QaSourceReference {
|
|
235
|
+
/** Stable canonical identity after normalization. */
|
|
236
|
+
id: string;
|
|
237
|
+
|
|
238
|
+
kind: QaSourceKind;
|
|
239
|
+
title: string;
|
|
240
|
+
|
|
241
|
+
/** Click target for remote sources. */
|
|
242
|
+
uri?: string;
|
|
243
|
+
|
|
244
|
+
/** Canonical path for workspace/local sources. */
|
|
245
|
+
path?: string;
|
|
246
|
+
|
|
247
|
+
/** Optional human-facing description/snippet. */
|
|
248
|
+
snippet?: string;
|
|
249
|
+
|
|
250
|
+
/** All useful ranges/anchors retained after dedupe. */
|
|
251
|
+
locations: QaSourceLocation[];
|
|
252
|
+
|
|
253
|
+
/** Why this object is considered a source. */
|
|
254
|
+
evidence: QaSourceEvidence;
|
|
255
|
+
|
|
256
|
+
/** All parent/subagent/tool origins merged by dedupe. */
|
|
257
|
+
origins: QaSourceOrigin[];
|
|
258
|
+
|
|
259
|
+
/** Higher means more suitable for main Sources UI. */
|
|
260
|
+
score: number;
|
|
261
|
+
|
|
262
|
+
metadata?: Record<string, unknown>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface QaTurnSources {
|
|
266
|
+
version: 1;
|
|
267
|
+
sessionId: string;
|
|
268
|
+
turn: number;
|
|
269
|
+
|
|
270
|
+
sources: QaSourceReference[];
|
|
271
|
+
|
|
272
|
+
/** Optional low-value candidates hidden from the default source list. */
|
|
273
|
+
discovered?: QaSourceReference[];
|
|
274
|
+
|
|
275
|
+
complete: boolean;
|
|
276
|
+
|
|
277
|
+
incompleteOrigins?: Array<{
|
|
278
|
+
subagentRunId?: string;
|
|
279
|
+
provider?: string;
|
|
280
|
+
reason: string;
|
|
281
|
+
}>;
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 6.1. Why keep `origins[]`
|
|
286
|
+
|
|
287
|
+
Если один и тот же документ:
|
|
288
|
+
|
|
289
|
+
- прочитал parent;
|
|
290
|
+
- затем прочитал subagent A;
|
|
291
|
+
- затем открыл subagent B;
|
|
292
|
+
|
|
293
|
+
в UI должен оставаться один source card, но debug details могут показать `Used by 3 agent runs`.
|
|
294
|
+
|
|
295
|
+
### 6.2. Stable ID rules
|
|
296
|
+
|
|
297
|
+
Recommended canonical IDs:
|
|
298
|
+
|
|
299
|
+
```text
|
|
300
|
+
file:<workspace-relative-canonical-path>
|
|
301
|
+
web:<canonical-url>
|
|
302
|
+
jira:<issue-key>
|
|
303
|
+
confluence:<space-or-page-id>
|
|
304
|
+
knowledge:<backend>:<document-id>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Line ranges **не включать** в primary source ID.
|
|
308
|
+
|
|
309
|
+
Несколько ranges одного файла должны merge'иться в `locations[]`.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 7. Source collection
|
|
314
|
+
|
|
315
|
+
### 7.1. Preferred integration point
|
|
316
|
+
|
|
317
|
+
Primary collection path:
|
|
318
|
+
|
|
319
|
+
1. observe completed tool results;
|
|
320
|
+
2. resolve structured presentation/result metadata;
|
|
321
|
+
3. pass the final typed result through an extractor;
|
|
322
|
+
4. add normalized references to the active turn collector.
|
|
323
|
+
|
|
324
|
+
Implementation should prefer DSH's final tool-result observation seam / durable `tool/result` event rather than parsing call text before execution.
|
|
325
|
+
|
|
326
|
+
Why:
|
|
327
|
+
|
|
328
|
+
- result already knows whether the call succeeded;
|
|
329
|
+
- final redirect URL can be known for web fetch;
|
|
330
|
+
- read presentation contains structured path/lines;
|
|
331
|
+
- web search presentation contains structured source objects;
|
|
332
|
+
- session replay can reproduce persisted result metadata.
|
|
333
|
+
|
|
334
|
+
### 7.2. Extractor registry
|
|
335
|
+
|
|
336
|
+
Do not write one large `switch(toolName)`.
|
|
337
|
+
|
|
338
|
+
Suggested registry:
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
interface SourceExtractorContext {
|
|
342
|
+
toolName: string;
|
|
343
|
+
args: unknown;
|
|
344
|
+
result: unknown;
|
|
345
|
+
presentation?: unknown;
|
|
346
|
+
origin: QaSourceOrigin;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
interface SourceExtractor {
|
|
350
|
+
matches(ctx: SourceExtractorContext): boolean;
|
|
351
|
+
extract(ctx: SourceExtractorContext): QaSourceReference[];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
class SourceExtractorRegistry {
|
|
355
|
+
register(extractor: SourceExtractor): Disposable;
|
|
356
|
+
extract(ctx: SourceExtractorContext): QaSourceReference[];
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Possible modules:
|
|
361
|
+
|
|
362
|
+
```text
|
|
363
|
+
provenance/extractors/
|
|
364
|
+
read-file.ts
|
|
365
|
+
file-search.ts
|
|
366
|
+
web-search.ts
|
|
367
|
+
web-fetch.ts
|
|
368
|
+
jira.ts
|
|
369
|
+
confluence.ts
|
|
370
|
+
knowledge.ts
|
|
371
|
+
generic-presented-location.ts
|
|
372
|
+
reported-source.ts
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### 7.3. Built-in DSH file read
|
|
376
|
+
|
|
377
|
+
For a successful `ReadResultView`:
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
{
|
|
381
|
+
card: ("read", path, offset, lines, totalLines, lang);
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
create one evidence source:
|
|
386
|
+
|
|
387
|
+
```text
|
|
388
|
+
kind = file/code
|
|
389
|
+
path = path
|
|
390
|
+
evidence = read
|
|
391
|
+
lineStart = first returned line
|
|
392
|
+
lineEnd = last returned line
|
|
393
|
+
score = high
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Classification:
|
|
397
|
+
|
|
398
|
+
- documentation-like extension (`.md`, `.markdown`, `.rst`, `.adoc`, `.txt`) → `file`;
|
|
399
|
+
- source-code extension → `code`;
|
|
400
|
+
- unknown textual → `file`.
|
|
401
|
+
|
|
402
|
+
### 7.4. grep/content search
|
|
403
|
+
|
|
404
|
+
`grep` results are useful, but every matched file should not automatically become a high-confidence source.
|
|
405
|
+
|
|
406
|
+
Recommended behavior:
|
|
407
|
+
|
|
408
|
+
- file only appears in grep result and is never opened/read → `evidence='discovered'`;
|
|
409
|
+
- later `read` on the same file promotes/merges it into evidence source;
|
|
410
|
+
- optional config may include grep-only files if the grep snippets themselves materially formed the answer.
|
|
411
|
+
|
|
412
|
+
Default: keep them in `discovered`, not main Sources.
|
|
413
|
+
|
|
414
|
+
### 7.5. glob/path search/listing
|
|
415
|
+
|
|
416
|
+
Treat as navigation only.
|
|
417
|
+
|
|
418
|
+
Do not show paths from glob/list results in main Sources unless they are subsequently read.
|
|
419
|
+
|
|
420
|
+
### 7.6. `web_search`
|
|
421
|
+
|
|
422
|
+
DSH already exposes a structured `WebSearchResultView.sources` list.
|
|
423
|
+
|
|
424
|
+
Important: these are search results, not necessarily pages meaningfully consumed by the agent.
|
|
425
|
+
|
|
426
|
+
Therefore:
|
|
427
|
+
|
|
428
|
+
- initially add each search result as `evidence='discovered'` with low score;
|
|
429
|
+
- if the same URL is later fetched/opened, promote to `evidence='fetched'`;
|
|
430
|
+
- if the provider's search result itself is the only retrieval mechanism and returns substantive snippets used by the model, allow a bounded number of top search sources to be promoted by policy.
|
|
431
|
+
|
|
432
|
+
Default policy proposal:
|
|
433
|
+
|
|
434
|
+
```yaml
|
|
435
|
+
sources:
|
|
436
|
+
webSearch:
|
|
437
|
+
showDiscovered: false
|
|
438
|
+
promoteSearchResultsWithoutFetch: true
|
|
439
|
+
maxPromotedPerSearch: 5
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
This avoids current situations like `Web · 72` unless those 72 sources were actually meaningful.
|
|
443
|
+
|
|
444
|
+
### 7.7. `web_fetch`
|
|
445
|
+
|
|
446
|
+
Successful fetch = strong evidence source.
|
|
447
|
+
|
|
448
|
+
Use final redirected URL if DSH supplies it.
|
|
449
|
+
|
|
450
|
+
```text
|
|
451
|
+
kind = web
|
|
452
|
+
evidence = fetched
|
|
453
|
+
score = very high
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Canonicalize URL before dedupe:
|
|
457
|
+
|
|
458
|
+
- lowercase host;
|
|
459
|
+
- strip known tracking query params (`utm_*`, etc.);
|
|
460
|
+
- normalize default port;
|
|
461
|
+
- preserve business-relevant query params;
|
|
462
|
+
- remove fragment for primary identity unless fragment identifies a distinct documentation section worth keeping as a location/anchor.
|
|
463
|
+
|
|
464
|
+
Do not aggressively remove arbitrary query params.
|
|
465
|
+
|
|
466
|
+
### 7.8. Jira
|
|
467
|
+
|
|
468
|
+
Support explicit Jira extractors for actual Jira/MCP tool names used in the deployment.
|
|
469
|
+
|
|
470
|
+
Canonical identity:
|
|
471
|
+
|
|
472
|
+
```text
|
|
473
|
+
jira:MDC-24929
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Data:
|
|
477
|
+
|
|
478
|
+
```ts
|
|
479
|
+
{
|
|
480
|
+
kind: 'jira',
|
|
481
|
+
title: 'MDC-24929 — Вибродоктор. Добавить метод GET /history',
|
|
482
|
+
uri: '<actual issue URL>',
|
|
483
|
+
locations: [{ jiraKey: 'MDC-24929' }],
|
|
484
|
+
evidence: 'queried'
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Only issue read/search-result objects actually returned to the model should count.
|
|
489
|
+
|
|
490
|
+
Avoid treating login pages, Jira search UI URL, board URLs, etc. as separate sources when a concrete issue is known.
|
|
491
|
+
|
|
492
|
+
### 7.9. Confluence
|
|
493
|
+
|
|
494
|
+
Canonical identity should prefer stable page ID over title URL.
|
|
495
|
+
|
|
496
|
+
```text
|
|
497
|
+
confluence:<page-id>
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Keep:
|
|
501
|
+
|
|
502
|
+
- page title;
|
|
503
|
+
- canonical page URL;
|
|
504
|
+
- page id;
|
|
505
|
+
- optional space key;
|
|
506
|
+
- optional anchor/section.
|
|
507
|
+
|
|
508
|
+
### 7.10. Custom knowledge tools
|
|
509
|
+
|
|
510
|
+
Provide a small adapter API so internal KB tools can register their own source extractor without modifying core collector code.
|
|
511
|
+
|
|
512
|
+
Example:
|
|
513
|
+
|
|
514
|
+
```ts
|
|
515
|
+
ctx.qaSources.registerExtractor({
|
|
516
|
+
id: 'company-kb',
|
|
517
|
+
matches: ({ toolName }) => toolName === 'kb_search' || toolName === 'kb_get',
|
|
518
|
+
extract: ...,
|
|
519
|
+
})
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
If exposing a cross-plugin service is too expensive for first version, keep registry internal but design its types so it can be promoted later.
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## 8. Subagent provenance
|
|
527
|
+
|
|
528
|
+
This is a required part of the feature, not a later enhancement.
|
|
529
|
+
|
|
530
|
+
### 8.1. Lineage registry
|
|
531
|
+
|
|
532
|
+
Observe DSH subagent lifecycle:
|
|
533
|
+
|
|
534
|
+
```text
|
|
535
|
+
subagent/start
|
|
536
|
+
subagent/end
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Maintain a runtime mapping:
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
interface SubagentLineage {
|
|
543
|
+
runId: string;
|
|
544
|
+
parentSessionId: string;
|
|
545
|
+
parentTurn: number;
|
|
546
|
+
childAgentId?: string;
|
|
547
|
+
childSessionId?: string;
|
|
548
|
+
provider?: string;
|
|
549
|
+
parentRunId?: string;
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Nested children must be supported recursively.
|
|
554
|
+
|
|
555
|
+
### 8.2. In-process / observable child agents
|
|
556
|
+
|
|
557
|
+
When a child session/agent is observable by the current Host:
|
|
558
|
+
|
|
559
|
+
- collect its tool sources exactly as for the parent;
|
|
560
|
+
- mark origin `role='subagent'`;
|
|
561
|
+
- associate the child source with the parent turn that initiated the delegation;
|
|
562
|
+
- on `subagent/end`, merge collected child evidence into parent `QaTurnSources`;
|
|
563
|
+
- retain `subagentRunId` in `origins[]`.
|
|
564
|
+
|
|
565
|
+
### 8.3. Nested subagents
|
|
566
|
+
|
|
567
|
+
If child A starts child B:
|
|
568
|
+
|
|
569
|
+
```text
|
|
570
|
+
Parent turn
|
|
571
|
+
└─ A
|
|
572
|
+
└─ B
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
sources from B should bubble to the original parent turn while retaining full origin lineage.
|
|
576
|
+
|
|
577
|
+
No duplicates should be created when A also reports B's source in its final answer.
|
|
578
|
+
|
|
579
|
+
### 8.4. Opaque/external child providers
|
|
580
|
+
|
|
581
|
+
Some providers may not expose internal tool-result traffic to the Host.
|
|
582
|
+
|
|
583
|
+
For these, automatic observation cannot guarantee complete provenance.
|
|
584
|
+
|
|
585
|
+
Add a fallback model-facing tool usable by subagents:
|
|
586
|
+
|
|
587
|
+
```text
|
|
588
|
+
qa_report_sources
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Suggested schema:
|
|
592
|
+
|
|
593
|
+
```ts
|
|
594
|
+
{
|
|
595
|
+
sources: Array<{
|
|
596
|
+
kind: "file" | "web" | "jira" | "confluence" | "knowledge" | "other";
|
|
597
|
+
title: string;
|
|
598
|
+
uri?: string;
|
|
599
|
+
path?: string;
|
|
600
|
+
lineStart?: number;
|
|
601
|
+
lineEnd?: number;
|
|
602
|
+
externalId?: string;
|
|
603
|
+
}>;
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
Properties:
|
|
608
|
+
|
|
609
|
+
- read-only;
|
|
610
|
+
- no external side effects;
|
|
611
|
+
- only appends provenance to current child run collector;
|
|
612
|
+
- can be called multiple times;
|
|
613
|
+
- duplicate-safe;
|
|
614
|
+
- never mutates workspace;
|
|
615
|
+
- preferably hidden/unavailable to ordinary `/qa` parent unless needed.
|
|
616
|
+
|
|
617
|
+
### 8.5. Companion skill / prompt guidance
|
|
618
|
+
|
|
619
|
+
Add a short internal instruction for delegated research:
|
|
620
|
+
|
|
621
|
+
```text
|
|
622
|
+
When you use sources that may not be observable by the parent harness,
|
|
623
|
+
report them with qa_report_sources before returning your result.
|
|
624
|
+
Do not manually format a "Sources" section unless explicitly requested.
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Important: this skill/tool path is **fallback**, not primary architecture.
|
|
628
|
+
|
|
629
|
+
The parent agent should not be asked to reconstruct child sources from prose.
|
|
630
|
+
|
|
631
|
+
### 8.6. Completeness flag
|
|
632
|
+
|
|
633
|
+
If a child provider is opaque and no `qa_report_sources` calls were received:
|
|
634
|
+
|
|
635
|
+
```ts
|
|
636
|
+
complete = false;
|
|
637
|
+
incompleteOrigins.push({
|
|
638
|
+
subagentRunId,
|
|
639
|
+
provider,
|
|
640
|
+
reason:
|
|
641
|
+
"Child tool provenance is not observable and no structured source report was received.",
|
|
642
|
+
});
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
In normal QA mode this warning may stay hidden unless the source drawer is expanded into details/debug.
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## 9. Turn finalization and persistence
|
|
650
|
+
|
|
651
|
+
### 9.1. Do not add an extra LLM step by default
|
|
652
|
+
|
|
653
|
+
At the end of the turn the plugin should finalize sources itself.
|
|
654
|
+
|
|
655
|
+
Desired lifecycle:
|
|
656
|
+
|
|
657
|
+
```text
|
|
658
|
+
turn/start
|
|
659
|
+
collector.open(parent session, turn)
|
|
660
|
+
|
|
661
|
+
...tool results...
|
|
662
|
+
...subagent starts/results/ends...
|
|
663
|
+
|
|
664
|
+
turn/end
|
|
665
|
+
collector.finalize()
|
|
666
|
+
append durable qa/sources event
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### 9.2. Custom session event
|
|
670
|
+
|
|
671
|
+
Use a plugin-owned durable session event if current DSH plugin API allows SessionEventMap declaration merging.
|
|
672
|
+
|
|
673
|
+
Suggested event:
|
|
674
|
+
|
|
675
|
+
```ts
|
|
676
|
+
declare module "@deepseek-ai/dsh-session" {
|
|
677
|
+
interface SessionEventMap {
|
|
678
|
+
"qa/sources": QaTurnSources;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
The event should be log-only / non-message-producing.
|
|
684
|
+
|
|
685
|
+
It must not enter model conversation history.
|
|
686
|
+
|
|
687
|
+
### 9.3. Why persist it
|
|
688
|
+
|
|
689
|
+
Persistence is required so:
|
|
690
|
+
|
|
691
|
+
- session reload produces the same source list;
|
|
692
|
+
- UI does not have to re-run extraction over old raw tool text;
|
|
693
|
+
- source dedupe/order stays stable;
|
|
694
|
+
- source panel works for historical turns;
|
|
695
|
+
- future migrations can version the source payload.
|
|
696
|
+
|
|
697
|
+
### 9.4. Replay / migration
|
|
698
|
+
|
|
699
|
+
For old sessions without `qa/sources`:
|
|
700
|
+
|
|
701
|
+
Preferred fallback:
|
|
702
|
+
|
|
703
|
+
1. reconstruct from historical structured `tool/result` metadata where possible;
|
|
704
|
+
2. do not parse arbitrary final `## Источники` prose by default;
|
|
705
|
+
3. optionally allow one-time legacy parser behind config.
|
|
706
|
+
|
|
707
|
+
```yaml
|
|
708
|
+
sources:
|
|
709
|
+
legacy:
|
|
710
|
+
parseAssistantSourcesBlock: false
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
Reason: natural-language parsing would reintroduce the very ambiguity this feature is intended to remove.
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## 10. Dedupe and ranking
|
|
718
|
+
|
|
719
|
+
### 10.1. Dedupe
|
|
720
|
+
|
|
721
|
+
Merge same canonical source across:
|
|
722
|
+
|
|
723
|
+
- repeated reads;
|
|
724
|
+
- parent + child;
|
|
725
|
+
- multiple subagents;
|
|
726
|
+
- web search + web fetch;
|
|
727
|
+
- multiple line ranges;
|
|
728
|
+
- repeated Jira/Confluence fetches.
|
|
729
|
+
|
|
730
|
+
Merge rules:
|
|
731
|
+
|
|
732
|
+
```text
|
|
733
|
+
title -> prefer explicit resource title over filename/domain fallback
|
|
734
|
+
uri -> prefer canonical/final URL
|
|
735
|
+
locations -> union + compact overlapping line ranges
|
|
736
|
+
origins -> union
|
|
737
|
+
snippet -> keep best non-empty snippet
|
|
738
|
+
score -> max / recompute from strongest evidence
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 10.2. Evidence promotion
|
|
742
|
+
|
|
743
|
+
Example:
|
|
744
|
+
|
|
745
|
+
```text
|
|
746
|
+
web_search result -> discovered, score 20
|
|
747
|
+
web_fetch same URL -> fetched, score 100
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
Final source becomes one object with `evidence='fetched'`.
|
|
751
|
+
|
|
752
|
+
### 10.3. Suggested scoring
|
|
753
|
+
|
|
754
|
+
Initial policy, configurable later:
|
|
755
|
+
|
|
756
|
+
| Event | Score |
|
|
757
|
+
| ------------------------------- | ----: |
|
|
758
|
+
| explicit file read | 100 |
|
|
759
|
+
| web fetch | 100 |
|
|
760
|
+
| Jira issue read | 100 |
|
|
761
|
+
| Confluence page read | 100 |
|
|
762
|
+
| knowledge document get | 100 |
|
|
763
|
+
| structured subagent report | 90 |
|
|
764
|
+
| web search source without fetch | 55 |
|
|
765
|
+
| grep matched file without read | 30 |
|
|
766
|
+
| glob/path discovery | 10 |
|
|
767
|
+
|
|
768
|
+
### 10.4. Default display threshold
|
|
769
|
+
|
|
770
|
+
Main Sources UI should show `score >= 50` by default.
|
|
771
|
+
|
|
772
|
+
Lower-score objects remain available in diagnostics if retained.
|
|
773
|
+
|
|
774
|
+
---
|
|
775
|
+
|
|
776
|
+
## 11. Source count semantics
|
|
777
|
+
|
|
778
|
+
Current `Sources · 100` style count is not useful if it includes every discovery result.
|
|
779
|
+
|
|
780
|
+
Change semantics:
|
|
781
|
+
|
|
782
|
+
```text
|
|
783
|
+
Sources · N
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
where `N` = unique evidence sources shown by default after dedupe/promotion/filtering.
|
|
787
|
+
|
|
788
|
+
Optional debug row:
|
|
789
|
+
|
|
790
|
+
```text
|
|
791
|
+
Discovered · 73
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
should be collapsed/hidden unless explicitly enabled.
|
|
795
|
+
|
|
796
|
+
This makes the count understandable to a QA user.
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
800
|
+
## 12. Sources UI redesign
|
|
801
|
+
|
|
802
|
+
The UI should look like a product source browser, not raw tool activity.
|
|
803
|
+
|
|
804
|
+
### 12.1. Main drawer
|
|
805
|
+
|
|
806
|
+
Suggested layout:
|
|
807
|
+
|
|
808
|
+
```text
|
|
809
|
+
┌──────────────────────────────────────────┐
|
|
810
|
+
│ Sources ✕ │
|
|
811
|
+
│ 12 sources │
|
|
812
|
+
├──────────────────────────────────────────┤
|
|
813
|
+
│ Documentation · 4 │
|
|
814
|
+
│ │
|
|
815
|
+
│ [MD] technicalDiagnosis.md │
|
|
816
|
+
│ EAM / technicalDiagnosis │
|
|
817
|
+
│ lines 197–218 Used by agent │
|
|
818
|
+
│ │
|
|
819
|
+
│ [MD] products_eam.md │
|
|
820
|
+
│ AIS_Dispatcher / products │
|
|
821
|
+
│ │
|
|
822
|
+
│ Jira · 3 │
|
|
823
|
+
│ │
|
|
824
|
+
│ [J] MDC-24929 │
|
|
825
|
+
│ Вибродоктор. GET /history │
|
|
826
|
+
│ │
|
|
827
|
+
│ Confluence · 2 │
|
|
828
|
+
│ ... │
|
|
829
|
+
│ │
|
|
830
|
+
│ Web · 3 │
|
|
831
|
+
│ ... │
|
|
832
|
+
└──────────────────────────────────────────┘
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
### 12.2. Grouping
|
|
836
|
+
|
|
837
|
+
Default group order:
|
|
838
|
+
|
|
839
|
+
1. Documentation / Files
|
|
840
|
+
2. Code
|
|
841
|
+
3. Jira
|
|
842
|
+
4. Confluence
|
|
843
|
+
5. Knowledge
|
|
844
|
+
6. Web
|
|
845
|
+
7. Other
|
|
846
|
+
|
|
847
|
+
Do not sort alphabetically across categories.
|
|
848
|
+
|
|
849
|
+
Within group sort by:
|
|
850
|
+
|
|
851
|
+
1. score descending;
|
|
852
|
+
2. first use/order in turn;
|
|
853
|
+
3. title.
|
|
854
|
+
|
|
855
|
+
### 12.3. Card content
|
|
856
|
+
|
|
857
|
+
Each card may show:
|
|
858
|
+
|
|
859
|
+
- source icon / type badge;
|
|
860
|
+
- short title;
|
|
861
|
+
- path/domain/space;
|
|
862
|
+
- line range or issue key;
|
|
863
|
+
- optional favicon/service icon;
|
|
864
|
+
- optional `Subagent` indicator;
|
|
865
|
+
- optional `Used by N agents` only in details/tooltip;
|
|
866
|
+
- external-link icon for remote resources.
|
|
867
|
+
|
|
868
|
+
Avoid showing long raw URLs if title exists.
|
|
869
|
+
|
|
870
|
+
### 12.4. Source details
|
|
871
|
+
|
|
872
|
+
On hover/secondary details:
|
|
873
|
+
|
|
874
|
+
```text
|
|
875
|
+
Used by:
|
|
876
|
+
- Parent · read · turn 4
|
|
877
|
+
- Subagent "Search Jira" · jira_get
|
|
878
|
+
|
|
879
|
+
Locations:
|
|
880
|
+
- lines 197–218
|
|
881
|
+
- lines 355–371
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
This is useful for debugging but should not clutter the default card.
|
|
885
|
+
|
|
886
|
+
### 12.5. Footer under answer
|
|
887
|
+
|
|
888
|
+
Replace free-form model-written `## Источники` with a deterministic UI footer.
|
|
889
|
+
|
|
890
|
+
Example compact state:
|
|
891
|
+
|
|
892
|
+
```text
|
|
893
|
+
Sources 12 [Documentation 4] [Jira 3] [Confluence 2] [Web 3] ›
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
Expanded:
|
|
897
|
+
|
|
898
|
+
```text
|
|
899
|
+
Sources
|
|
900
|
+
|
|
901
|
+
Documentation
|
|
902
|
+
• technicalDiagnosis.md · 197–218
|
|
903
|
+
• products_eam.md
|
|
904
|
+
|
|
905
|
+
Jira
|
|
906
|
+
• MDC-24929 — Вибродоктор. GET /history
|
|
907
|
+
...
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Both footer and drawer must render from the same `QaTurnSources` object.
|
|
911
|
+
|
|
912
|
+
### 12.6. Avoid duplicated textual Sources block
|
|
913
|
+
|
|
914
|
+
Add system-prompt guidance:
|
|
915
|
+
|
|
916
|
+
```text
|
|
917
|
+
The QA surface renders collected sources automatically.
|
|
918
|
+
Do not append a manual Sources/Источники section unless the user explicitly asks for a textual bibliography.
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
Do not depend on this for data correctness; it is only to avoid duplicate visual output.
|
|
922
|
+
|
|
923
|
+
---
|
|
924
|
+
|
|
925
|
+
## 13. File source preview
|
|
926
|
+
|
|
927
|
+
### 13.1. Click behavior
|
|
928
|
+
|
|
929
|
+
Clicking a local file source opens a source preview surface (existing drawer/modal/dock area is acceptable).
|
|
930
|
+
|
|
931
|
+
Header example:
|
|
932
|
+
|
|
933
|
+
```text
|
|
934
|
+
Library / docs / User_Guide / EAM / ... / technicalDiagnosis.md
|
|
935
|
+
Raw/Rendered Download ✕
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
### 13.2. Markdown default
|
|
939
|
+
|
|
940
|
+
For extensions:
|
|
941
|
+
|
|
942
|
+
```text
|
|
943
|
+
.md
|
|
944
|
+
.markdown
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
open in `Rendered` mode by default.
|
|
948
|
+
|
|
949
|
+
For ordinary source code/text:
|
|
950
|
+
|
|
951
|
+
- use existing line-numbered/syntax-highlighted text/code preview;
|
|
952
|
+
- default to raw/code presentation.
|
|
953
|
+
|
|
954
|
+
### 13.3. Rendered / Raw toggle
|
|
955
|
+
|
|
956
|
+
For Markdown provide:
|
|
957
|
+
|
|
958
|
+
```text
|
|
959
|
+
[Rendered] [Raw]
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
State can be local to the preview component.
|
|
963
|
+
|
|
964
|
+
Optional: remember preference per extension for current browser session.
|
|
965
|
+
|
|
966
|
+
### 13.4. Reuse DSH `MarkdownText`
|
|
967
|
+
|
|
968
|
+
Preferred implementation: use the existing DSH `MarkdownText` primitive from `@deepseek-ai/dsh-client-ui-primitives` if accessible through the plugin's supported dependency/injection model.
|
|
969
|
+
|
|
970
|
+
Reasons:
|
|
971
|
+
|
|
972
|
+
- already supports GFM-style Markdown;
|
|
973
|
+
- already handles untrusted assistant Markdown safely;
|
|
974
|
+
- raw HTML is not executed;
|
|
975
|
+
- links are constrained/sanitized;
|
|
976
|
+
- style matches the rest of DSH Web.
|
|
977
|
+
|
|
978
|
+
Do not introduce a second unrelated Markdown renderer unless the existing primitive cannot be cleanly consumed from an out-of-tree plugin.
|
|
979
|
+
|
|
980
|
+
If direct cross-package value import violates the current DSH client-plugin boundary in the exact target version, either:
|
|
981
|
+
|
|
982
|
+
1. use the officially allowed package dependency if the package is a public primitive library;
|
|
983
|
+
2. expose an internal qa-surface renderer adapter;
|
|
984
|
+
3. as fallback, use a minimal Markdown renderer with equivalent sanitization behavior.
|
|
985
|
+
|
|
986
|
+
Do not render Markdown with `dangerouslySetInnerHTML` from unsanitized source.
|
|
987
|
+
|
|
988
|
+
### 13.5. Markdown features
|
|
989
|
+
|
|
990
|
+
Required:
|
|
991
|
+
|
|
992
|
+
- headings;
|
|
993
|
+
- paragraphs;
|
|
994
|
+
- lists;
|
|
995
|
+
- blockquotes;
|
|
996
|
+
- inline/fenced code;
|
|
997
|
+
- tables;
|
|
998
|
+
- links;
|
|
999
|
+
- emphasis/strong;
|
|
1000
|
+
- horizontal rules.
|
|
1001
|
+
|
|
1002
|
+
Nice-to-have:
|
|
1003
|
+
|
|
1004
|
+
- task lists;
|
|
1005
|
+
- KaTeX if already inherited from DSH renderer;
|
|
1006
|
+
- anchor navigation;
|
|
1007
|
+
- syntax highlighting inside fenced blocks.
|
|
1008
|
+
|
|
1009
|
+
### 13.6. Relative links inside Markdown
|
|
1010
|
+
|
|
1011
|
+
A Markdown document may contain:
|
|
1012
|
+
|
|
1013
|
+
```md
|
|
1014
|
+
[See architecture](../architecture.md)
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
Phase 1:
|
|
1018
|
+
|
|
1019
|
+
- do not navigate browser to arbitrary relative URL;
|
|
1020
|
+
- render it as a safe file-source link only if target can be resolved inside allowed QA workspace root;
|
|
1021
|
+
- otherwise show as non-clickable or raw link text.
|
|
1022
|
+
|
|
1023
|
+
Phase 2 may allow opening linked local Markdown documents in the same previewer, but only through Host-side canonical path validation.
|
|
1024
|
+
|
|
1025
|
+
### 13.7. Line highlighting
|
|
1026
|
+
|
|
1027
|
+
If source has a line range:
|
|
1028
|
+
|
|
1029
|
+
```text
|
|
1030
|
+
technicalDiagnosis.md · lines 197–218
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
Raw mode should scroll to and visually highlight that range.
|
|
1034
|
+
|
|
1035
|
+
Rendered Markdown cannot reliably map every original source line to exact DOM nodes after Markdown parsing.
|
|
1036
|
+
|
|
1037
|
+
Therefore:
|
|
1038
|
+
|
|
1039
|
+
- best effort: scroll to nearest heading/section if an anchor is available;
|
|
1040
|
+
- provide a visible `Referenced lines: 197–218` chip;
|
|
1041
|
+
- `View raw at lines 197–218` jumps to exact raw line range.
|
|
1042
|
+
|
|
1043
|
+
Do not fake exact rendered-line highlighting.
|
|
1044
|
+
|
|
1045
|
+
### 13.8. Large files
|
|
1046
|
+
|
|
1047
|
+
Avoid loading/rendering unlimited files in browser.
|
|
1048
|
+
|
|
1049
|
+
Suggested defaults:
|
|
1050
|
+
|
|
1051
|
+
```yaml
|
|
1052
|
+
sources:
|
|
1053
|
+
filePreview:
|
|
1054
|
+
maxBytes: 2_000_000
|
|
1055
|
+
maxMarkdownRenderBytes: 1_000_000
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
If too large:
|
|
1059
|
+
|
|
1060
|
+
```text
|
|
1061
|
+
This file is too large for rendered preview.
|
|
1062
|
+
[Open raw excerpt] [Download]
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
Use existing Host/file read service where possible rather than exposing a new unrestricted arbitrary-file HTTP endpoint.
|
|
1066
|
+
|
|
1067
|
+
---
|
|
1068
|
+
|
|
1069
|
+
## 14. Remote source behavior
|
|
1070
|
+
|
|
1071
|
+
### Web
|
|
1072
|
+
|
|
1073
|
+
Click opens canonical URL in new tab with safe `noopener/noreferrer` behavior.
|
|
1074
|
+
|
|
1075
|
+
Optional future enhancement: cached fetched-text preview, but not required now.
|
|
1076
|
+
|
|
1077
|
+
### Jira / Confluence
|
|
1078
|
+
|
|
1079
|
+
Default:
|
|
1080
|
+
|
|
1081
|
+
- open canonical application URL in new tab;
|
|
1082
|
+
- if a structured content payload was already returned by the connector/tool, optionally show an in-QA preview before external navigation.
|
|
1083
|
+
|
|
1084
|
+
Do not store credentials in the browser for source rendering.
|
|
1085
|
+
|
|
1086
|
+
---
|
|
1087
|
+
|
|
1088
|
+
## 15. Inline citations (optional phase)
|
|
1089
|
+
|
|
1090
|
+
This is not required for MVP, but the data model should allow it.
|
|
1091
|
+
|
|
1092
|
+
Possible model guidance:
|
|
1093
|
+
|
|
1094
|
+
```text
|
|
1095
|
+
When useful, cite collected sources by stable id:
|
|
1096
|
+
[[source:file:docs/foo.md]]
|
|
1097
|
+
[[source:jira:MDC-24929]]
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
UI converts them into compact citation chips such as `[1]`.
|
|
1101
|
+
|
|
1102
|
+
However do not make `QaTurnSources` dependent on inline citations.
|
|
1103
|
+
|
|
1104
|
+
Sources must still be collected even if the model emits zero citation markers.
|
|
1105
|
+
|
|
1106
|
+
No extra turn-stopping LLM rewrite by default.
|
|
1107
|
+
|
|
1108
|
+
---
|
|
1109
|
+
|
|
1110
|
+
## 16. Proposed configuration
|
|
1111
|
+
|
|
1112
|
+
```yaml
|
|
1113
|
+
sources:
|
|
1114
|
+
enabled: true
|
|
1115
|
+
|
|
1116
|
+
collect:
|
|
1117
|
+
parentAgent: true
|
|
1118
|
+
subagents: true
|
|
1119
|
+
persistTurnEvent: true
|
|
1120
|
+
|
|
1121
|
+
display:
|
|
1122
|
+
sidebar: true
|
|
1123
|
+
footer: true
|
|
1124
|
+
groupByKind: true
|
|
1125
|
+
showDiscovered: false
|
|
1126
|
+
showOriginBadges: false
|
|
1127
|
+
maxInitiallyVisiblePerGroup: 8
|
|
1128
|
+
|
|
1129
|
+
webSearch:
|
|
1130
|
+
promoteSearchResultsWithoutFetch: true
|
|
1131
|
+
maxPromotedPerSearch: 5
|
|
1132
|
+
|
|
1133
|
+
dedupe:
|
|
1134
|
+
normalizeUrls: true
|
|
1135
|
+
stripTrackingParams: true
|
|
1136
|
+
mergeFileRanges: true
|
|
1137
|
+
|
|
1138
|
+
filePreview:
|
|
1139
|
+
enabled: true
|
|
1140
|
+
markdownRenderedByDefault: true
|
|
1141
|
+
allowRawToggle: true
|
|
1142
|
+
maxBytes: 2000000
|
|
1143
|
+
maxMarkdownRenderBytes: 1000000
|
|
1144
|
+
|
|
1145
|
+
subagents:
|
|
1146
|
+
inheritSources: true
|
|
1147
|
+
enableReportToolFallback: true
|
|
1148
|
+
markIncompleteOpaqueRuns: true
|
|
1149
|
+
|
|
1150
|
+
legacy:
|
|
1151
|
+
parseAssistantSourcesBlock: false
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
All defaults should remain compatible with `/qa` read-only mode.
|
|
1155
|
+
|
|
1156
|
+
---
|
|
1157
|
+
|
|
1158
|
+
## 17. Suggested code structure
|
|
1159
|
+
|
|
1160
|
+
Adapt to the actual current plugin layout after repository audit.
|
|
1161
|
+
|
|
1162
|
+
```text
|
|
1163
|
+
plugins/dsh-qa-surface/
|
|
1164
|
+
src/
|
|
1165
|
+
host/
|
|
1166
|
+
provenance/
|
|
1167
|
+
types.ts
|
|
1168
|
+
collector.ts
|
|
1169
|
+
turn-store.ts
|
|
1170
|
+
normalize.ts
|
|
1171
|
+
dedupe.ts
|
|
1172
|
+
scoring.ts
|
|
1173
|
+
lineage.ts
|
|
1174
|
+
event.ts
|
|
1175
|
+
|
|
1176
|
+
extractors/
|
|
1177
|
+
registry.ts
|
|
1178
|
+
read-file.ts
|
|
1179
|
+
file-search.ts
|
|
1180
|
+
web-search.ts
|
|
1181
|
+
web-fetch.ts
|
|
1182
|
+
jira.ts
|
|
1183
|
+
confluence.ts
|
|
1184
|
+
knowledge.ts
|
|
1185
|
+
generic-location.ts
|
|
1186
|
+
reported.ts
|
|
1187
|
+
|
|
1188
|
+
tools/
|
|
1189
|
+
report-sources.ts
|
|
1190
|
+
|
|
1191
|
+
prompt/
|
|
1192
|
+
source-guidance.ts
|
|
1193
|
+
|
|
1194
|
+
remotes/
|
|
1195
|
+
sources.ts
|
|
1196
|
+
file-preview.ts
|
|
1197
|
+
|
|
1198
|
+
client/
|
|
1199
|
+
sources/
|
|
1200
|
+
SourcesPanel.tsx
|
|
1201
|
+
SourcesGroup.tsx
|
|
1202
|
+
SourceCard.tsx
|
|
1203
|
+
SourceFooter.tsx
|
|
1204
|
+
SourceIcon.tsx
|
|
1205
|
+
SourceDetails.tsx
|
|
1206
|
+
source-store.ts
|
|
1207
|
+
|
|
1208
|
+
preview/
|
|
1209
|
+
SourcePreview.tsx
|
|
1210
|
+
FilePreview.tsx
|
|
1211
|
+
MarkdownPreview.tsx
|
|
1212
|
+
RawFilePreview.tsx
|
|
1213
|
+
PreviewHeader.tsx
|
|
1214
|
+
|
|
1215
|
+
styles/
|
|
1216
|
+
sources.module.css
|
|
1217
|
+
preview.module.css
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
If the plugin already has host/client folders with different naming, preserve existing conventions instead of mechanically applying this tree.
|
|
1221
|
+
|
|
1222
|
+
---
|
|
1223
|
+
|
|
1224
|
+
## 18. Host/client contract
|
|
1225
|
+
|
|
1226
|
+
The browser should not infer sources from rendered chat nodes.
|
|
1227
|
+
|
|
1228
|
+
Preferred flow:
|
|
1229
|
+
|
|
1230
|
+
```text
|
|
1231
|
+
Host persisted qa/sources event
|
|
1232
|
+
↓
|
|
1233
|
+
existing session transport / remote projection
|
|
1234
|
+
↓
|
|
1235
|
+
client source store keyed by sessionId + turn
|
|
1236
|
+
↓
|
|
1237
|
+
SourcesPanel / SourceFooter
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
For file preview, use an explicit read-only Host capability with workspace-root validation if the existing DSH workspace-file read service cannot be reused.
|
|
1241
|
+
|
|
1242
|
+
Example narrow contract:
|
|
1243
|
+
|
|
1244
|
+
```ts
|
|
1245
|
+
interface QaSourcePreviewApi {
|
|
1246
|
+
readFile(input: {
|
|
1247
|
+
sessionId: string;
|
|
1248
|
+
path: string;
|
|
1249
|
+
maxBytes?: number;
|
|
1250
|
+
}): Promise<{
|
|
1251
|
+
path: string;
|
|
1252
|
+
content: string;
|
|
1253
|
+
size: number;
|
|
1254
|
+
truncated: boolean;
|
|
1255
|
+
mime?: string;
|
|
1256
|
+
}>;
|
|
1257
|
+
}
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
Security requirements:
|
|
1261
|
+
|
|
1262
|
+
- canonicalize path on Host;
|
|
1263
|
+
- ensure path is inside allowed workspace/root;
|
|
1264
|
+
- reject symlink escape according to the deployment's existing file security semantics;
|
|
1265
|
+
- no arbitrary absolute-path browser API;
|
|
1266
|
+
- no write endpoint;
|
|
1267
|
+
- source preview request does not relax QA sandbox/tool policy.
|
|
1268
|
+
|
|
1269
|
+
---
|
|
1270
|
+
|
|
1271
|
+
## 19. Source presentation mapping
|
|
1272
|
+
|
|
1273
|
+
Suggested source labels/icons:
|
|
1274
|
+
|
|
1275
|
+
| Kind | Label | Example |
|
|
1276
|
+
| ------------ | -------------------- | ---------------------------- |
|
|
1277
|
+
| `file` | Documentation / File | `technicalDiagnosis.md` |
|
|
1278
|
+
| `code` | Code | `ActivitySectionTypeEnum.cs` |
|
|
1279
|
+
| `jira` | Jira | `MDC-24929` |
|
|
1280
|
+
| `confluence` | Confluence | `Вибродиагностика MM3` |
|
|
1281
|
+
| `knowledge` | Knowledge | KB document |
|
|
1282
|
+
| `web` | Web | GitHub / docs site |
|
|
1283
|
+
| `other` | Other | connector-specific source |
|
|
1284
|
+
|
|
1285
|
+
Web source title fallback order:
|
|
1286
|
+
|
|
1287
|
+
1. tool/provider supplied title;
|
|
1288
|
+
2. page title cached by fetch;
|
|
1289
|
+
3. hostname + shortened path;
|
|
1290
|
+
4. raw URL only as last resort.
|
|
1291
|
+
|
|
1292
|
+
File title:
|
|
1293
|
+
|
|
1294
|
+
- primary: basename;
|
|
1295
|
+
- secondary: shortened directory path;
|
|
1296
|
+
- never show huge absolute host filesystem path to QA user if workspace-relative path is available.
|
|
1297
|
+
|
|
1298
|
+
---
|
|
1299
|
+
|
|
1300
|
+
## 20. UI states
|
|
1301
|
+
|
|
1302
|
+
### No sources
|
|
1303
|
+
|
|
1304
|
+
Do not render an empty `Sources` footer.
|
|
1305
|
+
|
|
1306
|
+
Sidebar source action may be hidden/disabled.
|
|
1307
|
+
|
|
1308
|
+
### Loading / active turn
|
|
1309
|
+
|
|
1310
|
+
Sources may appear incrementally while the turn is running, but mark them as provisional.
|
|
1311
|
+
|
|
1312
|
+
At `turn/end`, replace with finalized snapshot.
|
|
1313
|
+
|
|
1314
|
+
### Error
|
|
1315
|
+
|
|
1316
|
+
Source preview failure:
|
|
1317
|
+
|
|
1318
|
+
```text
|
|
1319
|
+
Could not open this source.
|
|
1320
|
+
The source may have moved or is no longer accessible.
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
Do not expose raw Host stack traces.
|
|
1324
|
+
|
|
1325
|
+
### Truncated source
|
|
1326
|
+
|
|
1327
|
+
Show a small `Partial` badge if file/web result was truncated.
|
|
1328
|
+
|
|
1329
|
+
### Incomplete subagent provenance
|
|
1330
|
+
|
|
1331
|
+
Only show in debug/details by default:
|
|
1332
|
+
|
|
1333
|
+
```text
|
|
1334
|
+
Some delegated-source provenance is unavailable.
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
---
|
|
1338
|
+
|
|
1339
|
+
## 21. Testing plan
|
|
1340
|
+
|
|
1341
|
+
### 21.1. Unit: normalization
|
|
1342
|
+
|
|
1343
|
+
Test:
|
|
1344
|
+
|
|
1345
|
+
- URL canonicalization;
|
|
1346
|
+
- tracking parameter removal;
|
|
1347
|
+
- Jira key canonicalization;
|
|
1348
|
+
- Confluence page-id identity;
|
|
1349
|
+
- workspace path normalization;
|
|
1350
|
+
- line-range compaction.
|
|
1351
|
+
|
|
1352
|
+
### 21.2. Unit: dedupe
|
|
1353
|
+
|
|
1354
|
+
Cases:
|
|
1355
|
+
|
|
1356
|
+
- same URL from search + fetch;
|
|
1357
|
+
- same file read twice at overlapping ranges;
|
|
1358
|
+
- same Jira issue by parent + child;
|
|
1359
|
+
- same source from nested subagents;
|
|
1360
|
+
- stronger evidence promotes weaker evidence.
|
|
1361
|
+
|
|
1362
|
+
### 21.3. Unit: extractors
|
|
1363
|
+
|
|
1364
|
+
Fixtures for:
|
|
1365
|
+
|
|
1366
|
+
- DSH `ReadResultView`;
|
|
1367
|
+
- DSH `SearchResultView`;
|
|
1368
|
+
- DSH `WebSearchResultView`;
|
|
1369
|
+
- DSH `WebFetchResultView`;
|
|
1370
|
+
- Jira connector payload;
|
|
1371
|
+
- Confluence connector payload;
|
|
1372
|
+
- structured `qa_report_sources` input.
|
|
1373
|
+
|
|
1374
|
+
### 21.4. Integration: turn lifecycle
|
|
1375
|
+
|
|
1376
|
+
Scenario:
|
|
1377
|
+
|
|
1378
|
+
```text
|
|
1379
|
+
user prompt
|
|
1380
|
+
→ parent reads docs/a.md
|
|
1381
|
+
→ parent web_search returns 10 results
|
|
1382
|
+
→ parent fetches 2 results
|
|
1383
|
+
→ turn ends
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
Expected:
|
|
1387
|
+
|
|
1388
|
+
- `docs/a.md` included;
|
|
1389
|
+
- 2 fetched web pages included;
|
|
1390
|
+
- at most configured number of search-only results included;
|
|
1391
|
+
- no duplicates;
|
|
1392
|
+
- persisted `qa/sources` event exists.
|
|
1393
|
+
|
|
1394
|
+
### 21.5. Integration: subagent inheritance
|
|
1395
|
+
|
|
1396
|
+
Scenario:
|
|
1397
|
+
|
|
1398
|
+
```text
|
|
1399
|
+
parent
|
|
1400
|
+
→ starts child A
|
|
1401
|
+
→ reads docs/a.md
|
|
1402
|
+
→ reads Jira MDC-1
|
|
1403
|
+
→ child ends
|
|
1404
|
+
→ parent reads docs/a.md
|
|
1405
|
+
→ parent ends
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1408
|
+
Expected:
|
|
1409
|
+
|
|
1410
|
+
- one `docs/a.md` card with parent + child origins;
|
|
1411
|
+
- one Jira card;
|
|
1412
|
+
- parent turn source bundle owns both.
|
|
1413
|
+
|
|
1414
|
+
### 21.6. Integration: nested subagent
|
|
1415
|
+
|
|
1416
|
+
Parent → A → B, B reads source.
|
|
1417
|
+
|
|
1418
|
+
Expected source appears in parent turn with nested origin chain preserved.
|
|
1419
|
+
|
|
1420
|
+
### 21.7. Integration: opaque provider fallback
|
|
1421
|
+
|
|
1422
|
+
Opaque child calls `qa_report_sources`.
|
|
1423
|
+
|
|
1424
|
+
Expected reported source merged into parent bundle.
|
|
1425
|
+
|
|
1426
|
+
Opaque child does not report.
|
|
1427
|
+
|
|
1428
|
+
Expected `complete=false` and incomplete origin metadata.
|
|
1429
|
+
|
|
1430
|
+
### 21.8. UI: source panel
|
|
1431
|
+
|
|
1432
|
+
Test:
|
|
1433
|
+
|
|
1434
|
+
- grouping;
|
|
1435
|
+
- counts;
|
|
1436
|
+
- dedupe;
|
|
1437
|
+
- collapsed/expanded groups;
|
|
1438
|
+
- long titles;
|
|
1439
|
+
- source click;
|
|
1440
|
+
- external-link behavior;
|
|
1441
|
+
- subagent badge/details;
|
|
1442
|
+
- zero-state.
|
|
1443
|
+
|
|
1444
|
+
### 21.9. UI: Markdown preview
|
|
1445
|
+
|
|
1446
|
+
Test `.md` file with:
|
|
1447
|
+
|
|
1448
|
+
- headings;
|
|
1449
|
+
- lists;
|
|
1450
|
+
- table;
|
|
1451
|
+
- fenced code;
|
|
1452
|
+
- links;
|
|
1453
|
+
- HTML/script attempt;
|
|
1454
|
+
- large document;
|
|
1455
|
+
- rendered/raw switch;
|
|
1456
|
+
- referenced line range.
|
|
1457
|
+
|
|
1458
|
+
Unsafe HTML/script must not execute.
|
|
1459
|
+
|
|
1460
|
+
### 21.10. Reload/replay
|
|
1461
|
+
|
|
1462
|
+
Reload browser/session.
|
|
1463
|
+
|
|
1464
|
+
Expected source order and counts remain identical without re-running tools.
|
|
1465
|
+
|
|
1466
|
+
---
|
|
1467
|
+
|
|
1468
|
+
## 22. Acceptance criteria
|
|
1469
|
+
|
|
1470
|
+
Feature is complete when all of the following hold:
|
|
1471
|
+
|
|
1472
|
+
1. Final assistant response no longer needs to manually produce a Sources section.
|
|
1473
|
+
2. A turn that reads/fetches known resources produces a structured `QaTurnSources` bundle automatically.
|
|
1474
|
+
3. Sources panel and answer footer use the same bundle.
|
|
1475
|
+
4. Repeated sources are deduplicated.
|
|
1476
|
+
5. Parent and observable subagent sources are merged into one turn-level source set.
|
|
1477
|
+
6. Nested subagent sources are inherited.
|
|
1478
|
+
7. Opaque subagents have `qa_report_sources` fallback and completeness tracking.
|
|
1479
|
+
8. Web search results do not automatically flood the main source list with every candidate.
|
|
1480
|
+
9. `Sources · N` reflects unique visible evidence sources, not raw tool-result count.
|
|
1481
|
+
10. File source cards open a safe file preview.
|
|
1482
|
+
11. `.md` and `.markdown` open rendered by default.
|
|
1483
|
+
12. Markdown preview offers `Rendered / Raw` toggle.
|
|
1484
|
+
13. Raw mode can jump to referenced line range.
|
|
1485
|
+
14. Markdown rendering does not execute raw HTML/JS.
|
|
1486
|
+
15. Session reload restores the same source bundle from persisted state.
|
|
1487
|
+
16. Source collection does not require an extra LLM call at end of every turn.
|
|
1488
|
+
17. `/qa` remains read-only; source preview introduces no write/control capability.
|
|
1489
|
+
18. Existing non-QA DSH UI remains unaffected unless explicitly wired to the new source service.
|
|
1490
|
+
|
|
1491
|
+
---
|
|
1492
|
+
|
|
1493
|
+
## 23. Implementation phases
|
|
1494
|
+
|
|
1495
|
+
### Phase 0 — audit current `dsh-qa-surface`
|
|
1496
|
+
|
|
1497
|
+
Before coding:
|
|
1498
|
+
|
|
1499
|
+
- identify current source-panel implementation;
|
|
1500
|
+
- identify how current DSH Sources/Activity data reaches QA UI;
|
|
1501
|
+
- identify available tool presentation metadata in the pinned DSH version;
|
|
1502
|
+
- identify exact subagent lifecycle payloads;
|
|
1503
|
+
- identify existing file preview/read remote that can be reused;
|
|
1504
|
+
- identify whether `MarkdownText` is safely consumable by the out-of-tree client plugin.
|
|
1505
|
+
|
|
1506
|
+
Do not duplicate an existing DSH service if current version already exposes equivalent structured source/file-preview capabilities.
|
|
1507
|
+
|
|
1508
|
+
### Phase 1 — structured parent sources
|
|
1509
|
+
|
|
1510
|
+
Implement:
|
|
1511
|
+
|
|
1512
|
+
- canonical types;
|
|
1513
|
+
- extractor registry;
|
|
1514
|
+
- read/web extractors;
|
|
1515
|
+
- dedupe/ranking;
|
|
1516
|
+
- turn collector;
|
|
1517
|
+
- persisted `qa/sources`;
|
|
1518
|
+
- simple source panel bound to structured bundle.
|
|
1519
|
+
|
|
1520
|
+
No subagents yet only if needed to keep the first PR reviewable, but types must already support origins.
|
|
1521
|
+
|
|
1522
|
+
### Phase 2 — subagents
|
|
1523
|
+
|
|
1524
|
+
Implement:
|
|
1525
|
+
|
|
1526
|
+
- `subagent/start/end` lineage;
|
|
1527
|
+
- child collection;
|
|
1528
|
+
- nested inheritance;
|
|
1529
|
+
- `qa_report_sources` fallback;
|
|
1530
|
+
- completeness metadata.
|
|
1531
|
+
|
|
1532
|
+
### Phase 3 — polished Sources UX
|
|
1533
|
+
|
|
1534
|
+
Implement:
|
|
1535
|
+
|
|
1536
|
+
- grouped cards;
|
|
1537
|
+
- compact footer;
|
|
1538
|
+
- source details;
|
|
1539
|
+
- useful count semantics;
|
|
1540
|
+
- hide discovery noise;
|
|
1541
|
+
- icons/badges.
|
|
1542
|
+
|
|
1543
|
+
### Phase 4 — file preview + Markdown
|
|
1544
|
+
|
|
1545
|
+
Implement:
|
|
1546
|
+
|
|
1547
|
+
- click-to-preview;
|
|
1548
|
+
- secure Host file read/reuse existing service;
|
|
1549
|
+
- `Rendered / Raw`;
|
|
1550
|
+
- DSH `MarkdownText` reuse;
|
|
1551
|
+
- line-range jump in raw mode;
|
|
1552
|
+
- size limits.
|
|
1553
|
+
|
|
1554
|
+
### Phase 5 — optional inline citations
|
|
1555
|
+
|
|
1556
|
+
Only after structured source data is stable.
|
|
1557
|
+
|
|
1558
|
+
Do not block MVP on inline citation generation.
|
|
1559
|
+
|
|
1560
|
+
---
|
|
1561
|
+
|
|
1562
|
+
## 24. Migration from current textual Sources behavior
|
|
1563
|
+
|
|
1564
|
+
During transition:
|
|
1565
|
+
|
|
1566
|
+
1. keep current source panel available behind feature flag if needed;
|
|
1567
|
+
2. enable structured collector;
|
|
1568
|
+
3. compare structured source bundle to current UI during development;
|
|
1569
|
+
4. switch Sources panel to structured bundle;
|
|
1570
|
+
5. add system guidance suppressing manual `Источники` block;
|
|
1571
|
+
6. remove any code that parses assistant prose into Sources;
|
|
1572
|
+
7. retain optional legacy parser only for historical sessions if genuinely needed.
|
|
1573
|
+
|
|
1574
|
+
Feature flags example:
|
|
1575
|
+
|
|
1576
|
+
```yaml
|
|
1577
|
+
sources:
|
|
1578
|
+
implementation: structured # structured | legacy
|
|
1579
|
+
```
|
|
1580
|
+
|
|
1581
|
+
Remove `legacy` after migration period if no longer required.
|
|
1582
|
+
|
|
1583
|
+
---
|
|
1584
|
+
|
|
1585
|
+
## 25. Important implementation constraints
|
|
1586
|
+
|
|
1587
|
+
### Do
|
|
1588
|
+
|
|
1589
|
+
- treat session/tool metadata as authority;
|
|
1590
|
+
- preserve provenance across replay;
|
|
1591
|
+
- reuse DSH presentation metadata;
|
|
1592
|
+
- keep Host-side path validation;
|
|
1593
|
+
- keep source UI deterministic;
|
|
1594
|
+
- store source origin information even if default UI hides it;
|
|
1595
|
+
- make source extractors independently testable;
|
|
1596
|
+
- preserve current plugin's out-of-tree installability.
|
|
1597
|
+
|
|
1598
|
+
### Do not
|
|
1599
|
+
|
|
1600
|
+
- regex-parse arbitrary final assistant text as the main source mechanism;
|
|
1601
|
+
- require the parent LLM to copy child sources into its final answer;
|
|
1602
|
+
- show every `web_search` result as an equal source;
|
|
1603
|
+
- expose an arbitrary file-read endpoint to the browser;
|
|
1604
|
+
- use unsanitized `dangerouslySetInnerHTML` for Markdown;
|
|
1605
|
+
- make source collection depend on a second completion after `turn/end`;
|
|
1606
|
+
- fork DSH core for logic that can live in the plugin.
|
|
1607
|
+
|
|
1608
|
+
---
|
|
1609
|
+
|
|
1610
|
+
## 26. Notes for coding agent
|
|
1611
|
+
|
|
1612
|
+
1. Start by reading the current `plugins/dsh-qa-surface` source. This SPEC describes target behavior, not assumed current file names.
|
|
1613
|
+
2. Verify APIs against the exact DSH version pinned by the monorepo before coding; upstream DSH is moving quickly.
|
|
1614
|
+
3. Prefer typed DSH tool presentation metadata over tool-name-specific text parsing.
|
|
1615
|
+
4. Keep extraction adapters narrow; Jira/Confluence payloads may vary by MCP/provider.
|
|
1616
|
+
5. If the current DSH Sources panel already exposes a structured source model that covers part of this design, reuse/adapt it instead of building a parallel source universe.
|
|
1617
|
+
6. Add tests with representative real payload fixtures from the currently configured Jira/Confluence/web tools.
|
|
1618
|
+
7. Keep all source-preview capabilities read-only and compatible with the existing `/qa` lockdown policy.
|
|
1619
|
+
8. If implementation discovers a required upstream gap, document the gap before introducing a runtime patch.
|
|
1620
|
+
|
|
1621
|
+
---
|
|
1622
|
+
|
|
1623
|
+
## 27. Relevant upstream DSH references
|
|
1624
|
+
|
|
1625
|
+
These are architecture references for implementation; verify against the exact pinned commit/version during development.
|
|
1626
|
+
|
|
1627
|
+
- DSH tool subsystem / execution pipeline:
|
|
1628
|
+
`https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/tools.md`
|
|
1629
|
+
|
|
1630
|
+
- Structured tool presentation types (`ReadResultView`, `WebSearchResultView`, `WebFetchResultView`, file/search views):
|
|
1631
|
+
`https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/presentation.ts`
|
|
1632
|
+
|
|
1633
|
+
- Session event model / extensible `SessionEventMap`:
|
|
1634
|
+
`https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/session.md`
|
|
1635
|
+
|
|
1636
|
+
- Subagent lifecycle (`subagent/start`, `subagent/end`):
|
|
1637
|
+
`https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/subagent.md`
|
|
1638
|
+
|
|
1639
|
+
- Web client architecture:
|
|
1640
|
+
`https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/web-client.md`
|
|
1641
|
+
|
|
1642
|
+
- DSH Markdown primitive / UI primitives documentation:
|
|
1643
|
+
`https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/client/ui-primitives`
|
|
1644
|
+
|
|
1645
|
+
---
|
|
1646
|
+
|
|
1647
|
+
## 28. Desired end-state example
|
|
1648
|
+
|
|
1649
|
+
User asks a QA question.
|
|
1650
|
+
|
|
1651
|
+
Internally:
|
|
1652
|
+
|
|
1653
|
+
```text
|
|
1654
|
+
parent
|
|
1655
|
+
├─ read docs/User_Guide/EAM/.../technicalDiagnosis.md:197-218
|
|
1656
|
+
├─ subagent "Jira research"
|
|
1657
|
+
│ ├─ Jira MDC-24929
|
|
1658
|
+
│ ├─ Jira MDC-23617
|
|
1659
|
+
│ └─ Confluence page 12345
|
|
1660
|
+
├─ web_search -> 10 candidates
|
|
1661
|
+
└─ web_fetch -> 2 actual pages
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
Assistant only writes the answer.
|
|
1665
|
+
|
|
1666
|
+
No manually generated bibliography is required.
|
|
1667
|
+
|
|
1668
|
+
QA UI renders:
|
|
1669
|
+
|
|
1670
|
+
```text
|
|
1671
|
+
Ответ ...
|
|
1672
|
+
|
|
1673
|
+
────────────────────────────────────────
|
|
1674
|
+
Sources · 7
|
|
1675
|
+
Documentation 1 · Jira 2 · Confluence 1 · Web 2 · ...
|
|
1676
|
+
```
|
|
1677
|
+
|
|
1678
|
+
Opening `technicalDiagnosis.md` shows:
|
|
1679
|
+
|
|
1680
|
+
```text
|
|
1681
|
+
[Rendered] [Raw]
|
|
1682
|
+
|
|
1683
|
+
# Technical Diagnosis
|
|
1684
|
+
|
|
1685
|
+
...proper formatted Markdown...
|
|
1686
|
+
```
|
|
1687
|
+
|
|
1688
|
+
`Raw` jumps to lines `197–218`.
|
|
1689
|
+
|
|
1690
|
+
The Sources sidebar shows the exact same seven canonical source objects, grouped and deduplicated, including sources obtained by the subagent.
|
|
1691
|
+
|
|
1692
|
+
That is the target behavior.
|