@yadsh/dsh-qa-surface 0.6.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +163 -7
  2. package/capability-policy.json +1 -1
  3. package/lib/access/capability-catalog.js +144 -0
  4. package/lib/access/capability-catalog.js.map +1 -0
  5. package/lib/access/model.js +379 -0
  6. package/lib/access/model.js.map +1 -0
  7. package/lib/access/role-repository.js +195 -0
  8. package/lib/access/role-repository.js.map +1 -0
  9. package/lib/access/service.js +380 -0
  10. package/lib/access/service.js.map +1 -0
  11. package/lib/access/skill-metadata.js +234 -0
  12. package/lib/access/skill-metadata.js.map +1 -0
  13. package/lib/account-remotes.js +20 -10
  14. package/lib/account-remotes.js.map +1 -1
  15. package/lib/accounts/errors.js.map +1 -1
  16. package/lib/accounts/file.js.map +1 -1
  17. package/lib/accounts/store.js +202 -3
  18. package/lib/accounts/store.js.map +1 -1
  19. package/lib/admin/conversation-log.js +256 -0
  20. package/lib/admin/conversation-log.js.map +1 -0
  21. package/lib/admin/index.js +17 -0
  22. package/lib/admin/index.js.map +1 -0
  23. package/lib/admin/metrics.js +103 -0
  24. package/lib/admin/metrics.js.map +1 -0
  25. package/lib/admin/paging.js +23 -0
  26. package/lib/admin/paging.js.map +1 -0
  27. package/lib/admin/permissions.js +50 -0
  28. package/lib/admin/permissions.js.map +1 -0
  29. package/lib/admin/quality-store.js +485 -0
  30. package/lib/admin/quality-store.js.map +1 -0
  31. package/lib/admin/queue.js +107 -0
  32. package/lib/admin/queue.js.map +1 -0
  33. package/lib/admin/redaction.js +45 -0
  34. package/lib/admin/redaction.js.map +1 -0
  35. package/lib/admin/service.js +897 -0
  36. package/lib/admin/service.js.map +1 -0
  37. package/lib/admin/session-log.js +142 -0
  38. package/lib/admin/session-log.js.map +1 -0
  39. package/lib/cli.js +9 -6
  40. package/lib/cli.js.map +1 -1
  41. package/lib/client/QaSessionController.js +28 -2
  42. package/lib/client/QaSessionController.js.map +1 -1
  43. package/lib/client/QaSurface.js +143 -5
  44. package/lib/client/QaSurface.js.map +1 -1
  45. package/lib/client/QaTranscriptAdapter.js +1 -0
  46. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  47. package/lib/client/admin/QaAdmin.js +458 -0
  48. package/lib/client/admin/QaAdmin.js.map +1 -0
  49. package/lib/client/admin/format.js +177 -0
  50. package/lib/client/admin/format.js.map +1 -0
  51. package/lib/client/admin/pages/Conversations.js +114 -0
  52. package/lib/client/admin/pages/Conversations.js.map +1 -0
  53. package/lib/client/admin/pages/Overview.js +24 -0
  54. package/lib/client/admin/pages/Overview.js.map +1 -0
  55. package/lib/client/admin/pages/Quality.js +71 -0
  56. package/lib/client/admin/pages/Quality.js.map +1 -0
  57. package/lib/client/admin/pages/Review.js +93 -0
  58. package/lib/client/admin/pages/Review.js.map +1 -0
  59. package/lib/client/admin/pages/Users.js +95 -0
  60. package/lib/client/admin/pages/Users.js.map +1 -0
  61. package/lib/client/admin/routes.js +143 -0
  62. package/lib/client/admin/routes.js.map +1 -0
  63. package/lib/client/admin/shared.js +91 -0
  64. package/lib/client/admin/shared.js.map +1 -0
  65. package/lib/client/admin/styles.js +111 -0
  66. package/lib/client/admin/styles.js.map +1 -0
  67. package/lib/client/components/QaChangelog.js +53 -0
  68. package/lib/client/components/QaChangelog.js.map +1 -1
  69. package/lib/client/components/QaHeader.js +3 -3
  70. package/lib/client/components/QaHeader.js.map +1 -1
  71. package/lib/client/components/QaMessage.js +61 -2
  72. package/lib/client/components/QaMessage.js.map +1 -1
  73. package/lib/client/create-session.js +1 -1
  74. package/lib/client/create-session.js.map +1 -1
  75. package/lib/client/index.js +48 -1
  76. package/lib/client/index.js.map +1 -1
  77. package/lib/client/role/RoleSelector.js +31 -0
  78. package/lib/client/role/RoleSelector.js.map +1 -0
  79. package/lib/client/session-sources.js +37 -1
  80. package/lib/client/session-sources.js.map +1 -1
  81. package/lib/client/settings/card.js +2 -2
  82. package/lib/client/settings/card.js.map +1 -1
  83. package/lib/client/settings/sections.js +0 -1
  84. package/lib/client/settings/sections.js.map +1 -1
  85. package/lib/client/settings-extensions/index.js +1 -0
  86. package/lib/client/settings-extensions/index.js.map +1 -1
  87. package/lib/client/settings-extensions/user-session.js +62 -0
  88. package/lib/client/settings-extensions/user-session.js.map +1 -0
  89. package/lib/client/styles.js +31 -0
  90. package/lib/client/styles.js.map +1 -1
  91. package/lib/client/types.js.map +1 -1
  92. package/lib/client.js +9993 -4848
  93. package/lib/client.js.map +1 -1
  94. package/lib/config-resolvers/defaults.js +0 -2
  95. package/lib/config-resolvers/defaults.js.map +1 -1
  96. package/lib/config.js +0 -249
  97. package/lib/config.js.map +1 -1
  98. package/lib/enforcement/skill-policy.js +172 -0
  99. package/lib/enforcement/skill-policy.js.map +1 -0
  100. package/lib/enforcement/tool-grants.js +151 -0
  101. package/lib/enforcement/tool-grants.js.map +1 -0
  102. package/lib/index.js +224 -42
  103. package/lib/index.js.map +1 -1
  104. package/lib/personal-skills/service.js +10 -3
  105. package/lib/personal-skills/service.js.map +1 -1
  106. package/lib/qa-tools/index.js +4 -0
  107. package/lib/qa-tools/index.js.map +1 -1
  108. package/lib/resolve-config.js +0 -2
  109. package/lib/resolve-config.js.map +1 -1
  110. package/lib/secure-session.js +71 -17
  111. package/lib/secure-session.js.map +1 -1
  112. package/lib/typert.host.js +2237 -202
  113. package/lib/typert.remote-client.d.ts +49 -3
  114. package/lib/typert.remote-client.js +1863 -183
  115. package/lib/types/access/capability-catalog.d.ts +37 -0
  116. package/lib/types/access/model.d.ts +60 -0
  117. package/lib/types/access/role-repository.d.ts +32 -0
  118. package/lib/types/access/service.d.ts +57 -0
  119. package/lib/types/access/skill-metadata.d.ts +53 -0
  120. package/lib/types/account-remotes.d.ts +6 -0
  121. package/lib/types/accounts/errors.d.ts +1 -1
  122. package/lib/types/accounts/file.d.ts +16 -5
  123. package/lib/types/accounts/store.d.ts +83 -3
  124. package/lib/types/admin/conversation-log.d.ts +39 -0
  125. package/lib/types/admin/index.d.ts +24 -0
  126. package/lib/types/admin/metrics.d.ts +29 -0
  127. package/lib/types/admin/paging.d.ts +33 -0
  128. package/lib/types/admin/permissions.d.ts +10 -0
  129. package/lib/types/admin/quality-store.d.ts +82 -0
  130. package/lib/types/admin/queue.d.ts +21 -0
  131. package/lib/types/admin/redaction.d.ts +29 -0
  132. package/lib/types/admin/service.d.ts +105 -0
  133. package/lib/types/admin/session-log.d.ts +53 -0
  134. package/lib/types/client/QaSessionController.d.ts +12 -1
  135. package/lib/types/client/QaSurface.d.ts +7 -1
  136. package/lib/types/client/admin/QaAdmin.d.ts +17 -0
  137. package/lib/types/client/admin/format.d.ts +43 -0
  138. package/lib/types/client/admin/pages/Conversations.d.ts +21 -0
  139. package/lib/types/client/admin/pages/Overview.d.ts +13 -0
  140. package/lib/types/client/admin/pages/Quality.d.ts +18 -0
  141. package/lib/types/client/admin/pages/Review.d.ts +15 -0
  142. package/lib/types/client/admin/pages/Users.d.ts +22 -0
  143. package/lib/types/client/admin/routes.d.ts +60 -0
  144. package/lib/types/client/admin/shared.d.ts +53 -0
  145. package/lib/types/client/admin/styles.d.ts +11 -0
  146. package/lib/types/client/components/QaHeader.d.ts +5 -1
  147. package/lib/types/client/components/QaMessage.d.ts +14 -1
  148. package/lib/types/client/create-session.d.ts +2 -0
  149. package/lib/types/client/role/RoleSelector.d.ts +14 -0
  150. package/lib/types/client/session-sources.d.ts +9 -0
  151. package/lib/types/client/settings/sections.d.ts +0 -1
  152. package/lib/types/client/settings-extensions/index.d.ts +2 -0
  153. package/lib/types/client/settings-extensions/user-session.d.ts +59 -0
  154. package/lib/types/client/types.d.ts +39 -2
  155. package/lib/types/enforcement/skill-policy.d.ts +29 -0
  156. package/lib/types/enforcement/tool-grants.d.ts +84 -0
  157. package/lib/types/index.d.ts +62 -14
  158. package/lib/types/qa-tools/index.d.ts +2 -0
  159. package/lib/types/secure-session.d.ts +10 -1
  160. package/lib/types/types.d.ts +568 -5
  161. package/package.json +2 -2
  162. package/lib/client/settings/sections/documents.js +0 -88
  163. package/lib/client/settings/sections/documents.js.map +0 -1
  164. package/lib/documents/artifacts/ids.js +0 -43
  165. package/lib/documents/artifacts/ids.js.map +0 -1
  166. package/lib/documents/artifacts/manifest.js +0 -66
  167. package/lib/documents/artifacts/manifest.js.map +0 -1
  168. package/lib/documents/artifacts/store.js +0 -169
  169. package/lib/documents/artifacts/store.js.map +0 -1
  170. package/lib/documents/capabilities.js +0 -93
  171. package/lib/documents/capabilities.js.map +0 -1
  172. package/lib/documents/config.js +0 -302
  173. package/lib/documents/config.js.map +0 -1
  174. package/lib/documents/defaults.js +0 -78
  175. package/lib/documents/defaults.js.map +0 -1
  176. package/lib/documents/errors.js +0 -101
  177. package/lib/documents/errors.js.map +0 -1
  178. package/lib/documents/index.js +0 -80
  179. package/lib/documents/index.js.map +0 -1
  180. package/lib/documents/inspect/facts.js +0 -161
  181. package/lib/documents/inspect/facts.js.map +0 -1
  182. package/lib/documents/inspect/zip.js +0 -108
  183. package/lib/documents/inspect/zip.js.map +0 -1
  184. package/lib/documents/markdown/directives.js +0 -135
  185. package/lib/documents/markdown/directives.js.map +0 -1
  186. package/lib/documents/markdown/frontmatter.js +0 -35
  187. package/lib/documents/markdown/frontmatter.js.map +0 -1
  188. package/lib/documents/markdown/normalize.js +0 -113
  189. package/lib/documents/markdown/normalize.js.map +0 -1
  190. package/lib/documents/orchestrator/assets.js +0 -243
  191. package/lib/documents/orchestrator/assets.js.map +0 -1
  192. package/lib/documents/orchestrator/convert-document.js +0 -186
  193. package/lib/documents/orchestrator/convert-document.js.map +0 -1
  194. package/lib/documents/orchestrator/create-document.js +0 -292
  195. package/lib/documents/orchestrator/create-document.js.map +0 -1
  196. package/lib/documents/orchestrator/extract-document.js +0 -162
  197. package/lib/documents/orchestrator/extract-document.js.map +0 -1
  198. package/lib/documents/orchestrator/inspect-document.js +0 -89
  199. package/lib/documents/orchestrator/inspect-document.js.map +0 -1
  200. package/lib/documents/orchestrator/runtime-deps.js +0 -59
  201. package/lib/documents/orchestrator/runtime-deps.js.map +0 -1
  202. package/lib/documents/orchestrator/scope.js +0 -132
  203. package/lib/documents/orchestrator/scope.js.map +0 -1
  204. package/lib/documents/providers/docling.js +0 -247
  205. package/lib/documents/providers/docling.js.map +0 -1
  206. package/lib/documents/providers/libreoffice.js +0 -130
  207. package/lib/documents/providers/libreoffice.js.map +0 -1
  208. package/lib/documents/providers/markitdown.js +0 -89
  209. package/lib/documents/providers/markitdown.js.map +0 -1
  210. package/lib/documents/providers/pandoc.js +0 -161
  211. package/lib/documents/providers/pandoc.js.map +0 -1
  212. package/lib/documents/providers/process.js +0 -164
  213. package/lib/documents/providers/process.js.map +0 -1
  214. package/lib/documents/providers/registry.js +0 -152
  215. package/lib/documents/providers/registry.js.map +0 -1
  216. package/lib/documents/providers/shared.js +0 -75
  217. package/lib/documents/providers/shared.js.map +0 -1
  218. package/lib/documents/runtime.js +0 -114
  219. package/lib/documents/runtime.js.map +0 -1
  220. package/lib/documents/security/file-types.js +0 -188
  221. package/lib/documents/security/file-types.js.map +0 -1
  222. package/lib/documents/security/limits.js +0 -78
  223. package/lib/documents/security/limits.js.map +0 -1
  224. package/lib/documents/security/mime.js +0 -43
  225. package/lib/documents/security/mime.js.map +0 -1
  226. package/lib/documents/security/paths.js +0 -133
  227. package/lib/documents/security/paths.js.map +0 -1
  228. package/lib/documents/templates/registry.js +0 -147
  229. package/lib/documents/templates/registry.js.map +0 -1
  230. package/lib/documents/templates/resolver.js +0 -59
  231. package/lib/documents/templates/resolver.js.map +0 -1
  232. package/lib/documents/tools/convert.js +0 -125
  233. package/lib/documents/tools/convert.js.map +0 -1
  234. package/lib/documents/tools/create.js +0 -187
  235. package/lib/documents/tools/create.js.map +0 -1
  236. package/lib/documents/tools/index.js +0 -41
  237. package/lib/documents/tools/index.js.map +0 -1
  238. package/lib/documents/tools/inspect.js +0 -135
  239. package/lib/documents/tools/inspect.js.map +0 -1
  240. package/lib/documents/tools/shared.js +0 -88
  241. package/lib/documents/tools/shared.js.map +0 -1
  242. package/lib/documents/tools/to-markdown.js +0 -125
  243. package/lib/documents/tools/to-markdown.js.map +0 -1
  244. package/lib/documents/types.js +0 -12
  245. package/lib/documents/types.js.map +0 -1
  246. package/lib/types/client/settings/sections/documents.d.ts +0 -13
  247. package/lib/types/documents/artifacts/ids.d.ts +0 -14
  248. package/lib/types/documents/artifacts/manifest.d.ts +0 -32
  249. package/lib/types/documents/artifacts/store.d.ts +0 -67
  250. package/lib/types/documents/capabilities.d.ts +0 -32
  251. package/lib/types/documents/config.d.ts +0 -27
  252. package/lib/types/documents/defaults.d.ts +0 -161
  253. package/lib/types/documents/errors.d.ts +0 -42
  254. package/lib/types/documents/index.d.ts +0 -63
  255. package/lib/types/documents/inspect/facts.d.ts +0 -43
  256. package/lib/types/documents/inspect/zip.d.ts +0 -28
  257. package/lib/types/documents/markdown/directives.d.ts +0 -39
  258. package/lib/types/documents/markdown/frontmatter.d.ts +0 -15
  259. package/lib/types/documents/markdown/normalize.d.ts +0 -30
  260. package/lib/types/documents/orchestrator/assets.d.ts +0 -55
  261. package/lib/types/documents/orchestrator/convert-document.d.ts +0 -25
  262. package/lib/types/documents/orchestrator/create-document.d.ts +0 -18
  263. package/lib/types/documents/orchestrator/extract-document.d.ts +0 -24
  264. package/lib/types/documents/orchestrator/inspect-document.d.ts +0 -12
  265. package/lib/types/documents/orchestrator/runtime-deps.d.ts +0 -43
  266. package/lib/types/documents/orchestrator/scope.d.ts +0 -52
  267. package/lib/types/documents/providers/docling.d.ts +0 -54
  268. package/lib/types/documents/providers/libreoffice.d.ts +0 -40
  269. package/lib/types/documents/providers/markitdown.d.ts +0 -28
  270. package/lib/types/documents/providers/pandoc.d.ts +0 -42
  271. package/lib/types/documents/providers/process.d.ts +0 -54
  272. package/lib/types/documents/providers/registry.d.ts +0 -68
  273. package/lib/types/documents/providers/shared.d.ts +0 -27
  274. package/lib/types/documents/runtime.d.ts +0 -59
  275. package/lib/types/documents/security/file-types.d.ts +0 -39
  276. package/lib/types/documents/security/limits.d.ts +0 -24
  277. package/lib/types/documents/security/mime.d.ts +0 -13
  278. package/lib/types/documents/security/paths.d.ts +0 -38
  279. package/lib/types/documents/templates/registry.d.ts +0 -50
  280. package/lib/types/documents/templates/resolver.d.ts +0 -30
  281. package/lib/types/documents/tools/convert.d.ts +0 -9
  282. package/lib/types/documents/tools/create.d.ts +0 -14
  283. package/lib/types/documents/tools/index.d.ts +0 -33
  284. package/lib/types/documents/tools/inspect.d.ts +0 -9
  285. package/lib/types/documents/tools/shared.d.ts +0 -77
  286. package/lib/types/documents/tools/to-markdown.d.ts +0 -12
  287. package/lib/types/documents/types.d.ts +0 -294
package/README.md CHANGED
@@ -22,6 +22,9 @@ Session and Agent Loop.
22
22
  - pins locked sessions to the configured `read-only` or isolated
23
23
  `workspace-write` policy plus `approval=never` before Send is enabled;
24
24
  - applies a Host-side tool allow-list plus a monotonic execution guard;
25
+ - separates account authorization (`admin`/`user`) from the QA agent's active
26
+ subrole, with server-owned Common, per-role Tools and Skills, immutable
27
+ session snapshots, and a dedicated administration page at `/qa/admin`;
25
28
  - attaches its own QA tool catalog per agent only after the activation skill
26
29
  loads (`tools.dynamicActivation`), keeping every QA schema out of the initial
27
30
  request and restoring the catalog on resume from the session's own journal;
@@ -44,14 +47,17 @@ Session and Agent Loop.
44
47
  - ships an operator settings card (Settings → Plugins → plugin configuration →
45
48
  «Помощник QA») that edits the `qa-surface` namespace in place — route,
46
49
  branding, session, interface, lockdown, accounts, sources, attachments,
47
- documents, embedding — and reports the configuration the running Host
50
+ embedding — and reports the configuration the running Host
48
51
  resolved;
49
- - registers four document tools (`document_create`, `document_to_markdown`,
50
- `document_convert`, `document_inspect`) behind `documents.enabled`: the agent
51
- writes Markdown and receives DOCX/PDF, or hands over a DOCX/PDF and receives
52
- Markdown, while the pipeline owns every backend command line, keeps source,
53
- assets, outputs and a manifest in one artifact bundle, and is visible to a QA
54
- chat only through the tool allow-list;
52
+ - no longer owns the document pipeline: `document_create`,
53
+ `document_to_markdown`, `document_from_url`, `document_convert` and
54
+ `document_inspect` come from [`@yadsh/dsh-documents`](https://github.com/xarleyn/dsh-plugins/tree/main/plugins/dsh-documents#readme),
55
+ which a QA chat reaches exactly as before — same tool names, same allow-list
56
+ entry, same artifact layout. What moved with it is the configuration: the
57
+ pipeline is configured in that plugin's own `documents` namespace and card
58
+ (`QA_DOCUMENTS_*` environment variables became `DSH_DOCUMENTS_*`), and a
59
+ leftover `documents:` section under `qa-surface` is ignored with a
60
+ `documents.moved` warning in the Host log;
55
61
  - uses the existing same-origin DSH connection and trust boundary.
56
62
 
57
63
  It does not add another HTTP server, provider proxy, permissive CORS rule, or
@@ -571,6 +577,33 @@ export function apply(ctx: Context) {
571
577
  }
572
578
  ```
573
579
 
580
+ ### The signed-in account outside the dialog
581
+
582
+ A card mounted in the host's own settings (`settings.plugin.item`) has no panel
583
+ props to read the account from, so the same contract also publishes the session
584
+ as the `qaUserSession` client service: `checking`, `anonymous` or `authed` with
585
+ the bearer credential the principal-scoped QA remotes authorize with. It follows
586
+ the same account controller the pages use, so a card and the dialog never
587
+ disagree. Subscribe to it with `useSyncExternalStore` and render account-bound
588
+ controls only for `authed` — without an account every call would be refused, and
589
+ the credential is transport authentication only: never persist it, log it, or
590
+ put it in a URL, a tool argument or any model-visible value.
591
+
592
+ ```ts
593
+ import type { Context } from "@deepseek-ai/cordis"
594
+
595
+ export const inject = ["qaUserSession", "slots"]
596
+
597
+ export function apply(ctx: Context) {
598
+ ctx.effect(() =>
599
+ ctx.slots.inject("settings.plugin.item", () =>
600
+ ctx.slots.register(
601
+ { name: "settings.plugin.item", key: "my-namespace" },
602
+ MyCard,
603
+ )))
604
+ }
605
+ ```
606
+
574
607
  Subagents: the deployment may opt the delegation family (`subagent`,
575
608
  `subagent_fork`, `send_message`, `list_agents`, `interrupt_agent`) into the
576
609
  lockdown allow-list; the preset must mount them. Launches then render as
@@ -662,6 +695,129 @@ Host-installed integration plugins may add their own narrowly scoped tool names
662
695
  through the QA Surface service; those executors must independently resolve the
663
696
  owner-attested root principal and fail closed for unowned or child sessions.
664
697
 
698
+ ## Subroles and capability policies
699
+
700
+ With `accounts.enabled: true`, every QA session has exactly one active agent
701
+ subrole. The Host resolves its effective capabilities as the union of the
702
+ minimal system-required set, Common capabilities, and that one subrole; it
703
+ never unions all roles assigned to the user. The chosen role and effective
704
+ snapshot are stored with session ownership. Existing conversations never gain
705
+ new capabilities after an administrator edits a role, while a capability that
706
+ disappears from the running registry is revoked immediately.
707
+
708
+ Administrators manage subroles, Common Tools/Skills, user assignments and the
709
+ audit trail at `/qa/admin`. Capability choices come from the live tool and
710
+ skill registries; configured-but-missing entries remain visible and are not
711
+ deleted. Admin authorization affects only the management API and never grants
712
+ agent capabilities. `Preview as role` creates an ordinary session using the
713
+ selected role's real server-enforced policy and shows a persistent preview
714
+ banner.
715
+
716
+ The configuration lives in `$DSH_HOME/qa-capability-policies.json`; user
717
+ assignments and session snapshots stay with the existing account store. Tools
718
+ are filtered with an agent-scoped restriction and a pre-execution gate. Skills
719
+ use an agent-scoped `skill` consumer that publishes only the allow-listed
720
+ catalog and rejects direct out-of-policy loads with `SKILL_NOT_AVAILABLE`.
721
+ The browser selector is hidden for a single assigned role. Switching roles
722
+ after a meaningful turn requires confirmation and creates a new conversation.
723
+
724
+ ### Tools that arrive with a skill
725
+
726
+ A role's tools are split into two classes. `always` tools are visible from the
727
+ first model step. `skillGrantable` tools are a ceiling, not a grant: they stay
728
+ out of the model's tool list until an activated skill requires them, which
729
+ keeps a large catalog such as browser automation out of every step of every
730
+ conversation. An older flat `tools: []` list is read as `always`, so an
731
+ upgraded deployment never hands out more than it did before.
732
+
733
+ ### Skills declare their own audience
734
+
735
+ A skill describes itself inside the ordinary `metadata` block of its
736
+ `SKILL.md`, so the file stays a valid Agent Skill outside a QA deployment and
737
+ no upstream schema is forked:
738
+
739
+ ```yaml
740
+ ---
741
+ name: browser-research
742
+ description: Research websites through browser automation
743
+
744
+ metadata:
745
+ qa-surface:
746
+ version: 1
747
+ audience:
748
+ type: subroles # or: type: common
749
+ include: [analyst, presales]
750
+ tools:
751
+ requires: [browser_open, browser_click]
752
+ grant:
753
+ lifecycle: session # the only lifecycle in v1
754
+ requireAll: true
755
+ ---
756
+ ```
757
+
758
+ The audience decides visibility; the role ceiling decides what a skill may ever
759
+ receive. A load intersects the two, so a skill cannot widen its own access. A
760
+ skill that declares nothing stays `Unassigned` until an administrator assigns
761
+ it, which is safer than treating every new skill as Common.
762
+
763
+ Loading the instructions and widening the toolset is one operation. A strict
764
+ skill (`requireAll: true`) refuses to activate when one of its tools is
765
+ unavailable, and a best-effort skill activates with a visible warning that
766
+ names what it did not receive. The model's `skill` call and a typed
767
+ `/skill-name` activate the same grant; a `/name` naming a skill outside the
768
+ subrole is withdrawn before the step is assembled. Grants last for the current
769
+ agent, and several loaded skills union their tools — activating a second skill
770
+ never revokes what the first one still holds.
771
+
772
+ Administrators edit assignments as an overlay, never by rewriting `SKILL.md`: a
773
+ role can be added to or withdrawn from a declared audience, a skill can be
774
+ forced on for every role or disabled outright, and the Skills page shows the
775
+ declared audience next to the effective one with a
776
+ `Healthy`/`Degraded`/`Blocked` state. Every activation is recorded on the
777
+ session record with the requested, granted and denied tools, so a later review
778
+ can see what a conversation actually gained.
779
+ ## Administrative console
780
+
781
+ `/qa/admin` is the review and administration surface. It is part of the QA page
782
+ itself, not a separate application, and it is open to `admin` and `reviewer`
783
+ accounts. Reviewer sees conversations, the review queue, feedback and
784
+ analytics; only an administrator sees users, capability policies and audit.
785
+
786
+ The console covers the quality loop end to end:
787
+
788
+ - **Overview** — conversation, rating and review counters, the items that need
789
+ attention, and the newest signals.
790
+ - **Users** — authorization role, enabled/disabled status, assigned QA subroles
791
+ and the default one, plus per-account activity. Disabling an account is the
792
+ operation to reach for: historical conversations, feedback and reviews stay
793
+ attributed to their author, and the last enabled administrator cannot be
794
+ demoted, disabled or removed.
795
+ - **Conversations** — every conversation the deployment knows, filterable by
796
+ user, subrole, date range, rating and review state, with a viewer that reads
797
+ the stored transcript: messages in recorded order, tool calls with their
798
+ arguments, results and errors, per-message feedback, and the capability
799
+ snapshot frozen when the session started. A link can point at one message
800
+ (`/qa/admin/conversations/<id>/<seq>`).
801
+ - **Review queue** — what needs attention, derived from unanswered negative
802
+ feedback, explicitly queued conversations and failed tool calls. A reviewer
803
+ classifies issues across answer, context, tools, skills and access, sets a
804
+ severity, writes notes and names the remediation target.
805
+ - **Feedback** — every rating against the exact answer it judged, with the
806
+ optional reason and comment the user gave.
807
+ - **Analytics** — rating coverage, positive share overall and per subrole,
808
+ issue distribution and a daily trend. These are user-satisfaction signals;
809
+ the console never presents them as accuracy.
810
+ - **Audit** — one timeline of authorization changes, account status, subrole
811
+ assignments, policy edits and review verdicts, each with its before/after
812
+ image.
813
+
814
+ Authorization is a permission table, not an `isAdmin` flag, and every
815
+ administrative entry point names the permission it needs; the Host re-checks it
816
+ on the call it serves, so a hidden control is convenience rather than the
817
+ boundary. Review material is the most sensitive data the package handles, so
818
+ tool arguments and results are bounded previews with credential shapes masked,
819
+ behind a redactor a deployment can replace.
820
+
665
821
  ## Security and deployment
666
822
 
667
823
  `/qa` is a presentation boundary, not an authentication boundary. Protect it
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "version": 1,
3
- "description": "Reviewed capabilities in the shipped default QA tool allow-list.",
3
+ "description": "Minimal system-required tools inherited by every QA subrole. Common and role-specific grants are managed in /qa/admin.",
4
4
  "tools": []
5
5
  }
@@ -0,0 +1,144 @@
1
+ import { parseQaSkillMetadata } from "./skill-metadata.js";
2
+ function toolSource(name) {
3
+ if (name.startsWith("mcp__")) {
4
+ const server = name.slice(5).split("__", 1)[0];
5
+ return { kind: "mcp", ...(server === "" ? {} : { name: server }) };
6
+ }
7
+ if (name.startsWith("qa_") || name.startsWith("document_")) {
8
+ return { kind: "plugin", name: "dsh-qa-surface" };
9
+ }
10
+ return { kind: "core" };
11
+ }
12
+ function skillSource(skill) {
13
+ const kind = skill.provider.includes("filesystem") || skill.source.includes("project")
14
+ ? "filesystem"
15
+ : skill.source === "runtime"
16
+ ? "runtime"
17
+ : "plugin";
18
+ return { kind, name: skill.provider || skill.source };
19
+ }
20
+ /** Read-only adapter over the current DSH registries. */
21
+ export class QaCapabilityCatalog {
22
+ ctx;
23
+ additionalToolNames;
24
+ knownSubroles;
25
+ constructor(ctx, additionalToolNames = () => [],
26
+ /** Role ids used to report unknown subrole ids in skill metadata. */
27
+ knownSubroles = () => []) {
28
+ this.ctx = ctx;
29
+ this.additionalToolNames = additionalToolNames;
30
+ this.knownSubroles = knownSubroles;
31
+ }
32
+ async snapshot(agent) {
33
+ const toolDescriptors = this.ctx.tools
34
+ .schemas(agent)
35
+ .map((schema) => ({
36
+ type: "tool",
37
+ id: schema.name,
38
+ title: schema.name,
39
+ ...(schema.description === undefined
40
+ ? {}
41
+ : { description: schema.description }),
42
+ source: toolSource(schema.name),
43
+ status: "available",
44
+ }));
45
+ const mountedToolIds = new Set(toolDescriptors.map(({ id }) => id));
46
+ for (const id of this.additionalToolNames()) {
47
+ if (mountedToolIds.has(id))
48
+ continue;
49
+ mountedToolIds.add(id);
50
+ toolDescriptors.push({
51
+ type: "tool",
52
+ id,
53
+ title: id,
54
+ source: toolSource(id),
55
+ status: "available",
56
+ });
57
+ }
58
+ const registry = this.ctx.get("skills");
59
+ const lookup = {
60
+ ...(agent === undefined ? {} : { scope: agent }),
61
+ ...(agent?.session.header.cwd === undefined
62
+ ? {}
63
+ : { cwd: agent.session.header.cwd }),
64
+ };
65
+ const skillList = registry === undefined ? [] : (await registry.snapshot(lookup)).skills;
66
+ const skills = new Map(skillList.map((skill) => [skill.name, skill]));
67
+ const skillDescriptors = skillList.map((skill) => ({
68
+ type: "skill",
69
+ id: skill.name,
70
+ title: skill.name,
71
+ description: skill.description,
72
+ source: skillSource(skill),
73
+ status: "available",
74
+ modelInvocable: skill.invocation.modelInvocable,
75
+ userInvocable: skill.invocation.userInvocable,
76
+ }));
77
+ const descriptors = [...toolDescriptors, ...skillDescriptors].sort((left, right) => left.type.localeCompare(right.type) || left.id.localeCompare(right.id));
78
+ return Object.freeze({
79
+ descriptors: Object.freeze(descriptors),
80
+ toolIds: new Set(toolDescriptors.map(({ id }) => id)),
81
+ skillIds: new Set(skillList
82
+ .filter(({ invocation }) => invocation.modelInvocable)
83
+ .map(({ name }) => name)),
84
+ userSkillIds: new Set(skillList
85
+ .filter(({ invocation }) => invocation.userInvocable)
86
+ .map(({ name }) => name)),
87
+ skills,
88
+ skillMetadata: registry === undefined
89
+ ? new Map()
90
+ : await this.readSkillMetadata(registry, skillList, lookup),
91
+ });
92
+ }
93
+ /**
94
+ * Read `metadata.qa-surface` of every discovered skill.
95
+ *
96
+ * The registry exposes metadata on loaded definitions only, so discovery has
97
+ * to fetch each body once. A provider that fails or a skill that disappears
98
+ * between the two reads degrades to the fail-closed descriptor instead of
99
+ * failing the whole catalog.
100
+ */
101
+ async readSkillMetadata(registry, skillList, lookup) {
102
+ const known = new Set(this.knownSubroles());
103
+ const metadata = new Map();
104
+ for (const skill of skillList) {
105
+ let definition;
106
+ try {
107
+ definition = await registry.get(skill.name, lookup);
108
+ }
109
+ catch {
110
+ definition = undefined;
111
+ }
112
+ metadata.set(skill.name, parseQaSkillMetadata(skill.name, definition?.metadata, known));
113
+ }
114
+ return metadata;
115
+ }
116
+ }
117
+ export function withMissingCapabilities(descriptors, configuredTools, configuredSkills) {
118
+ const keys = new Set(descriptors.map(({ type, id }) => `${type}:${id}`));
119
+ const missing = [];
120
+ for (const id of configuredTools) {
121
+ if (!keys.has(`tool:${id}`)) {
122
+ missing.push({
123
+ type: "tool",
124
+ id,
125
+ title: id,
126
+ source: { kind: "plugin" },
127
+ status: "missing",
128
+ });
129
+ }
130
+ }
131
+ for (const id of configuredSkills) {
132
+ if (!keys.has(`skill:${id}`)) {
133
+ missing.push({
134
+ type: "skill",
135
+ id,
136
+ title: id,
137
+ source: { kind: "plugin" },
138
+ status: "missing",
139
+ });
140
+ }
141
+ }
142
+ return Object.freeze([...descriptors, ...missing]);
143
+ }
144
+ //# sourceMappingURL=capability-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-catalog.js","sourceRoot":"","sources":["../../src/access/capability-catalog.ts"],"names":[],"mappings":"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"}