@yadsh/dsh-qa-surface 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/README.md +148 -22
  2. package/compatibility.json +7 -3
  3. package/lib/access/capability-catalog.js +22 -6
  4. package/lib/access/capability-catalog.js.map +1 -1
  5. package/lib/access/model.js +36 -2
  6. package/lib/access/model.js.map +1 -1
  7. package/lib/access/service.js +19 -4
  8. package/lib/access/service.js.map +1 -1
  9. package/lib/account-remotes.js +4 -0
  10. package/lib/account-remotes.js.map +1 -1
  11. package/lib/accounts/database.js +49 -9
  12. package/lib/accounts/database.js.map +1 -1
  13. package/lib/accounts/errors.js.map +1 -1
  14. package/lib/accounts/file.js.map +1 -1
  15. package/lib/accounts/store.js +53 -8
  16. package/lib/accounts/store.js.map +1 -1
  17. package/lib/admin/permissions.js +6 -0
  18. package/lib/admin/permissions.js.map +1 -1
  19. package/lib/admin/quality-store.js +95 -19
  20. package/lib/admin/quality-store.js.map +1 -1
  21. package/lib/admin/service.js +432 -52
  22. package/lib/admin/service.js.map +1 -1
  23. package/lib/cli.js +24 -4
  24. package/lib/cli.js.map +1 -1
  25. package/lib/client/QaAccountsController.js +70 -5
  26. package/lib/client/QaAccountsController.js.map +1 -1
  27. package/lib/client/QaConfigController.js.map +1 -1
  28. package/lib/client/QaSessionController.js +626 -63
  29. package/lib/client/QaSessionController.js.map +1 -1
  30. package/lib/client/QaSurface.js +279 -43
  31. package/lib/client/QaSurface.js.map +1 -1
  32. package/lib/client/QaSurfaceGuard.js +2 -2
  33. package/lib/client/QaSurfaceGuard.js.map +1 -1
  34. package/lib/client/QaTranscriptAdapter.js +4 -2
  35. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  36. package/lib/client/admin/QaAdmin.js +59 -34
  37. package/lib/client/admin/QaAdmin.js.map +1 -1
  38. package/lib/client/admin/pages/Conversations.js +21 -14
  39. package/lib/client/admin/pages/Conversations.js.map +1 -1
  40. package/lib/client/admin/pages/ExpertMemory.js +210 -0
  41. package/lib/client/admin/pages/ExpertMemory.js.map +1 -0
  42. package/lib/client/admin/pages/Overview.js +5 -5
  43. package/lib/client/admin/pages/Overview.js.map +1 -1
  44. package/lib/client/admin/pages/Quality.js +6 -6
  45. package/lib/client/admin/pages/Quality.js.map +1 -1
  46. package/lib/client/admin/pages/Review.js +13 -9
  47. package/lib/client/admin/pages/Review.js.map +1 -1
  48. package/lib/client/admin/pages/SkillFiles.js +3 -3
  49. package/lib/client/admin/pages/SkillFiles.js.map +1 -1
  50. package/lib/client/admin/pages/Users.js +12 -12
  51. package/lib/client/admin/pages/Users.js.map +1 -1
  52. package/lib/client/admin/routes.js +4 -0
  53. package/lib/client/admin/routes.js.map +1 -1
  54. package/lib/client/admin/shared.js +57 -10
  55. package/lib/client/admin/shared.js.map +1 -1
  56. package/lib/client/admin/styles.js +3 -0
  57. package/lib/client/admin/styles.js.map +1 -1
  58. package/lib/client/attestation.js +11 -2
  59. package/lib/client/attestation.js.map +1 -1
  60. package/lib/client/audit/QaAuditDialog.js +2 -2
  61. package/lib/client/audit/QaAuditDialog.js.map +1 -1
  62. package/lib/client/audit/QaAuditRowBadge.js +1 -1
  63. package/lib/client/audit/QaAuditRowBadge.js.map +1 -1
  64. package/lib/client/components/Markdown.js +7 -2
  65. package/lib/client/components/Markdown.js.map +1 -1
  66. package/lib/client/components/QaAgentsDrawer.js +7 -5
  67. package/lib/client/components/QaAgentsDrawer.js.map +1 -1
  68. package/lib/client/components/QaApproval.js +2 -2
  69. package/lib/client/components/QaApproval.js.map +1 -1
  70. package/lib/client/components/QaAuthGate.js +6 -6
  71. package/lib/client/components/QaAuthGate.js.map +1 -1
  72. package/lib/client/components/QaChangelog.js +87 -0
  73. package/lib/client/components/QaChangelog.js.map +1 -1
  74. package/lib/client/components/QaComposer.js +9 -6
  75. package/lib/client/components/QaComposer.js.map +1 -1
  76. package/lib/client/components/QaFileAttachment.js +1 -1
  77. package/lib/client/components/QaFileAttachment.js.map +1 -1
  78. package/lib/client/components/QaFilesPanel.js +7 -7
  79. package/lib/client/components/QaFilesPanel.js.map +1 -1
  80. package/lib/client/components/QaHeader.js +4 -4
  81. package/lib/client/components/QaHeader.js.map +1 -1
  82. package/lib/client/components/QaMessage.js +12 -12
  83. package/lib/client/components/QaMessage.js.map +1 -1
  84. package/lib/client/components/QaModal.js +11 -2
  85. package/lib/client/components/QaModal.js.map +1 -1
  86. package/lib/client/components/QaQuestions.js +4 -4
  87. package/lib/client/components/QaQuestions.js.map +1 -1
  88. package/lib/client/components/QaQueueDock.js +137 -0
  89. package/lib/client/components/QaQueueDock.js.map +1 -0
  90. package/lib/client/components/QaRequestQueue.js +22 -0
  91. package/lib/client/components/QaRequestQueue.js.map +1 -0
  92. package/lib/client/components/QaRightRail.js +2 -2
  93. package/lib/client/components/QaRightRail.js.map +1 -1
  94. package/lib/client/components/QaSidebar.js +66 -6
  95. package/lib/client/components/QaSidebar.js.map +1 -1
  96. package/lib/client/components/QaSourcesPanel.js +10 -8
  97. package/lib/client/components/QaSourcesPanel.js.map +1 -1
  98. package/lib/client/components/QaThemeSwitcher.js +19 -0
  99. package/lib/client/components/QaThemeSwitcher.js.map +1 -0
  100. package/lib/client/components/QaTurnNotice.js +40 -0
  101. package/lib/client/components/QaTurnNotice.js.map +1 -0
  102. package/lib/client/components/QaTurnRail.js +3 -3
  103. package/lib/client/components/QaTurnRail.js.map +1 -1
  104. package/lib/client/components/QaWelcomeNotice.js +2 -6
  105. package/lib/client/components/QaWelcomeNotice.js.map +1 -1
  106. package/lib/client/components/QaWidthHandle.js +2 -2
  107. package/lib/client/components/QaWidthHandle.js.map +1 -1
  108. package/lib/client/components/QaWorkGroup.js +5 -5
  109. package/lib/client/components/QaWorkGroup.js.map +1 -1
  110. package/lib/client/components/QaWorkspaceBrowser.js +10 -10
  111. package/lib/client/components/QaWorkspaceBrowser.js.map +1 -1
  112. package/lib/client/components/VariantSwitcher.js +1 -1
  113. package/lib/client/components/VariantSwitcher.js.map +1 -1
  114. package/lib/client/failure-copy.js +26 -8
  115. package/lib/client/failure-copy.js.map +1 -1
  116. package/lib/client/focus-ring.js +193 -0
  117. package/lib/client/focus-ring.js.map +1 -0
  118. package/lib/client/index.js +58 -24
  119. package/lib/client/index.js.map +1 -1
  120. package/lib/client/lineage.js +3 -16
  121. package/lib/client/lineage.js.map +1 -1
  122. package/lib/client/markdown/CodeBlock.js +2 -2
  123. package/lib/client/markdown/CodeBlock.js.map +1 -1
  124. package/lib/client/markdown/blocks.js +118 -31
  125. package/lib/client/markdown/blocks.js.map +1 -1
  126. package/lib/client/markdown/math.js +1 -1
  127. package/lib/client/markdown/math.js.map +1 -1
  128. package/lib/client/markdown/render.js +32 -12
  129. package/lib/client/markdown/render.js.map +1 -1
  130. package/lib/client/markdown/source-chip.js +3 -3
  131. package/lib/client/markdown/source-chip.js.map +1 -1
  132. package/lib/client/notifications/notification-dispatcher.js +90 -0
  133. package/lib/client/notifications/notification-dispatcher.js.map +1 -0
  134. package/lib/client/notifications/preferences.js +61 -0
  135. package/lib/client/notifications/preferences.js.map +1 -0
  136. package/lib/client/notifications/turn-completion-source.js +82 -0
  137. package/lib/client/notifications/turn-completion-source.js.map +1 -0
  138. package/lib/client/notifications/use-turn-notifications.js +108 -0
  139. package/lib/client/notifications/use-turn-notifications.js.map +1 -0
  140. package/lib/client/panels/PanelHost.js +5 -5
  141. package/lib/client/panels/PanelHost.js.map +1 -1
  142. package/lib/client/panels/PanelLauncher.js +2 -2
  143. package/lib/client/panels/PanelLauncher.js.map +1 -1
  144. package/lib/client/project-session-state.js +156 -4
  145. package/lib/client/project-session-state.js.map +1 -1
  146. package/lib/client/rating-harvest.js +138 -0
  147. package/lib/client/rating-harvest.js.map +1 -0
  148. package/lib/client/role/RoleSelector.js +9 -4
  149. package/lib/client/role/RoleSelector.js.map +1 -1
  150. package/lib/client/settings/card.js +58 -17
  151. package/lib/client/settings/card.js.map +1 -1
  152. package/lib/client/settings/fields.js +11 -10
  153. package/lib/client/settings/fields.js.map +1 -1
  154. package/lib/client/settings/format.js +0 -9
  155. package/lib/client/settings/format.js.map +1 -1
  156. package/lib/client/settings/sections/access.js +5 -5
  157. package/lib/client/settings/sections/access.js.map +1 -1
  158. package/lib/client/settings/sections/accounts.js +10 -10
  159. package/lib/client/settings/sections/accounts.js.map +1 -1
  160. package/lib/client/settings/sections/attachments.js +6 -6
  161. package/lib/client/settings/sections/attachments.js.map +1 -1
  162. package/lib/client/settings/sections/branding.js +7 -7
  163. package/lib/client/settings/sections/branding.js.map +1 -1
  164. package/lib/client/settings/sections/common.js +4 -2
  165. package/lib/client/settings/sections/common.js.map +1 -1
  166. package/lib/client/settings/sections/docs.js +4 -3
  167. package/lib/client/settings/sections/docs.js.map +1 -1
  168. package/lib/client/settings/sections/embedding.js +2 -2
  169. package/lib/client/settings/sections/embedding.js.map +1 -1
  170. package/lib/client/settings/sections/interface.js +12 -12
  171. package/lib/client/settings/sections/interface.js.map +1 -1
  172. package/lib/client/settings/sections/lockdown.js +9 -9
  173. package/lib/client/settings/sections/lockdown.js.map +1 -1
  174. package/lib/client/settings/sections/notes.js +12 -12
  175. package/lib/client/settings/sections/notes.js.map +1 -1
  176. package/lib/client/settings/sections/session.js +15 -12
  177. package/lib/client/settings/sections/session.js.map +1 -1
  178. package/lib/client/settings/sections/slash.js +9 -9
  179. package/lib/client/settings/sections/slash.js.map +1 -1
  180. package/lib/client/settings/sections/sources.js +26 -26
  181. package/lib/client/settings/sections/sources.js.map +1 -1
  182. package/lib/client/settings/sections/status.js +2 -2
  183. package/lib/client/settings/sections/status.js.map +1 -1
  184. package/lib/client/settings/styles.js +15 -9
  185. package/lib/client/settings/styles.js.map +1 -1
  186. package/lib/client/slash/SlashPalette.js +4 -4
  187. package/lib/client/slash/SlashPalette.js.map +1 -1
  188. package/lib/client/styles.js +139 -66
  189. package/lib/client/styles.js.map +1 -1
  190. package/lib/client/theme-preference.js +162 -0
  191. package/lib/client/theme-preference.js.map +1 -0
  192. package/lib/client/types.js +8 -0
  193. package/lib/client/types.js.map +1 -1
  194. package/lib/client/use-session-ui-state.js +7 -5
  195. package/lib/client/use-session-ui-state.js.map +1 -1
  196. package/lib/client/user-settings/GeneralSettingsPage.js +6 -6
  197. package/lib/client/user-settings/GeneralSettingsPage.js.map +1 -1
  198. package/lib/client/user-settings/IntegrationTokensPage.js +6 -6
  199. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -1
  200. package/lib/client/user-settings/NotificationSettingsPage.js +107 -0
  201. package/lib/client/user-settings/NotificationSettingsPage.js.map +1 -0
  202. package/lib/client/user-settings/PasswordSettingsPage.js +1 -1
  203. package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -1
  204. package/lib/client/user-settings/ProfileSettingsPage.js +2 -2
  205. package/lib/client/user-settings/ProfileSettingsPage.js.map +1 -1
  206. package/lib/client/user-settings/SkillCatalog.js +3 -3
  207. package/lib/client/user-settings/SkillCatalog.js.map +1 -1
  208. package/lib/client/user-settings/SkillEditor.js +26 -8
  209. package/lib/client/user-settings/SkillEditor.js.map +1 -1
  210. package/lib/client/user-settings/SkillToolPicker.js +3 -3
  211. package/lib/client/user-settings/SkillToolPicker.js.map +1 -1
  212. package/lib/client/user-settings/SkillsSettingsPage.js +1 -1
  213. package/lib/client/user-settings/SkillsSettingsPage.js.map +1 -1
  214. package/lib/client/user-settings/StartersSettingsPage.js +6 -6
  215. package/lib/client/user-settings/StartersSettingsPage.js.map +1 -1
  216. package/lib/client/user-settings/UserSettingsDialog.js +7 -2
  217. package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
  218. package/lib/client/user-settings/copy.js +2 -0
  219. package/lib/client/user-settings/copy.js.map +1 -1
  220. package/lib/client/user-settings/draft.js +4 -1
  221. package/lib/client/user-settings/draft.js.map +1 -1
  222. package/lib/client/user-settings/fields.js +5 -5
  223. package/lib/client/user-settings/fields.js.map +1 -1
  224. package/lib/client.js +7483 -2075
  225. package/lib/client.js.map +1 -1
  226. package/lib/config-resolvers/defaults.js +7 -0
  227. package/lib/config-resolvers/defaults.js.map +1 -1
  228. package/lib/config-resolvers/lockdown.js +7 -0
  229. package/lib/config-resolvers/lockdown.js.map +1 -1
  230. package/lib/config-resolvers/notifications.js +15 -0
  231. package/lib/config-resolvers/notifications.js.map +1 -0
  232. package/lib/config-resolvers/session.js +11 -1
  233. package/lib/config-resolvers/session.js.map +1 -1
  234. package/lib/config.js +67 -18
  235. package/lib/config.js.map +1 -1
  236. package/lib/enforcement/tool-grants.js +6 -6
  237. package/lib/enforcement/tool-grants.js.map +1 -1
  238. package/lib/index.js +171 -39
  239. package/lib/index.js.map +1 -1
  240. package/lib/integration/answer.js +74 -8
  241. package/lib/integration/answer.js.map +1 -1
  242. package/lib/integration/expert-memory.js +39 -0
  243. package/lib/integration/expert-memory.js.map +1 -0
  244. package/lib/integration/host-runner.js +26 -14
  245. package/lib/integration/host-runner.js.map +1 -1
  246. package/lib/notifications.js +66 -0
  247. package/lib/notifications.js.map +1 -0
  248. package/lib/personal-skills/errors.js.map +1 -1
  249. package/lib/personal-skills/service.js +81 -7
  250. package/lib/personal-skills/service.js.map +1 -1
  251. package/lib/prompt-notes.js +2 -10
  252. package/lib/prompt-notes.js.map +1 -1
  253. package/lib/provenance/extractors.js +1 -1
  254. package/lib/provenance/extractors.js.map +1 -1
  255. package/lib/provenance/host-store.js +5 -1
  256. package/lib/provenance/host-store.js.map +1 -1
  257. package/lib/qa-tools/docs-tools.js +64 -11
  258. package/lib/qa-tools/docs-tools.js.map +1 -1
  259. package/lib/qa-tools/lifecycle.js +6 -3
  260. package/lib/qa-tools/lifecycle.js.map +1 -1
  261. package/lib/request-queue.js +32 -0
  262. package/lib/request-queue.js.map +1 -0
  263. package/lib/resolve-config.js +2 -0
  264. package/lib/resolve-config.js.map +1 -1
  265. package/lib/secure-session.js +7 -2
  266. package/lib/secure-session.js.map +1 -1
  267. package/lib/shared/settings.js +19 -2
  268. package/lib/shared/settings.js.map +1 -1
  269. package/lib/turn-failure-log.js +67 -0
  270. package/lib/turn-failure-log.js.map +1 -0
  271. package/lib/typert.host.js +1459 -647
  272. package/lib/typert.remote-client.d.ts +25 -9
  273. package/lib/typert.remote-client.js +1356 -636
  274. package/lib/types/access/capability-catalog.d.ts +20 -4
  275. package/lib/types/access/model.d.ts +23 -0
  276. package/lib/types/access/service.d.ts +2 -3
  277. package/lib/types/account-remotes.d.ts +8 -1
  278. package/lib/types/accounts/database.d.ts +13 -3
  279. package/lib/types/accounts/errors.d.ts +9 -1
  280. package/lib/types/accounts/file.d.ts +12 -0
  281. package/lib/types/accounts/store.d.ts +30 -2
  282. package/lib/types/admin/quality-store.d.ts +33 -0
  283. package/lib/types/admin/service.d.ts +119 -9
  284. package/lib/types/client/QaAccountsController.d.ts +37 -3
  285. package/lib/types/client/QaConfigController.d.ts +2 -2
  286. package/lib/types/client/QaSessionController.d.ts +199 -6
  287. package/lib/types/client/QaSurface.d.ts +6 -1
  288. package/lib/types/client/admin/pages/ExpertMemory.d.ts +19 -0
  289. package/lib/types/client/admin/routes.d.ts +3 -1
  290. package/lib/types/client/admin/shared.d.ts +20 -1
  291. package/lib/types/client/components/Markdown.d.ts +8 -1
  292. package/lib/types/client/components/QaHeader.d.ts +3 -1
  293. package/lib/types/client/components/QaModal.d.ts +7 -0
  294. package/lib/types/client/components/QaQueueDock.d.ts +27 -0
  295. package/lib/types/client/components/QaRequestQueue.d.ts +20 -0
  296. package/lib/types/client/components/QaThemeSwitcher.d.ts +14 -0
  297. package/lib/types/client/components/QaTurnNotice.d.ts +55 -0
  298. package/lib/types/client/focus-ring.d.ts +65 -0
  299. package/lib/types/client/lineage.d.ts +3 -13
  300. package/lib/types/client/markdown/blocks.d.ts +30 -2
  301. package/lib/types/client/markdown/render.d.ts +4 -1
  302. package/lib/types/client/notifications/notification-dispatcher.d.ts +68 -0
  303. package/lib/types/client/notifications/preferences.d.ts +44 -0
  304. package/lib/types/client/notifications/turn-completion-source.d.ts +114 -0
  305. package/lib/types/client/notifications/use-turn-notifications.d.ts +51 -0
  306. package/lib/types/client/project-session-state.d.ts +35 -1
  307. package/lib/types/client/rating-harvest.d.ts +49 -0
  308. package/lib/types/client/role/RoleSelector.d.ts +7 -1
  309. package/lib/types/client/settings/card.d.ts +36 -5
  310. package/lib/types/client/settings/fields.d.ts +24 -0
  311. package/lib/types/client/settings/format.d.ts +0 -5
  312. package/lib/types/client/settings/sections/common.d.ts +3 -1
  313. package/lib/types/client/settings/sections/session.d.ts +2 -1
  314. package/lib/types/client/settings/styles.d.ts +9 -4
  315. package/lib/types/client/settings-extensions/user-session.d.ts +1 -1
  316. package/lib/types/client/theme-preference.d.ts +69 -0
  317. package/lib/types/client/types.d.ts +36 -5
  318. package/lib/types/client/use-session-ui-state.d.ts +6 -4
  319. package/lib/types/client/user-settings/GeneralSettingsPage.d.ts +5 -5
  320. package/lib/types/client/user-settings/NotificationSettingsPage.d.ts +40 -0
  321. package/lib/types/client/user-settings/UserSettingsDialog.d.ts +12 -2
  322. package/lib/types/client/user-settings/draft.d.ts +4 -1
  323. package/lib/types/client/user-settings/fields.d.ts +7 -0
  324. package/lib/types/config-resolvers/notifications.d.ts +10 -0
  325. package/lib/types/config-resolvers/session.d.ts +6 -0
  326. package/lib/types/config.d.ts +20 -1
  327. package/lib/types/index.d.ts +70 -10
  328. package/lib/types/integration/answer.d.ts +27 -0
  329. package/lib/types/integration/expert-memory.d.ts +49 -0
  330. package/lib/types/notifications.d.ts +32 -0
  331. package/lib/types/personal-skills/errors.d.ts +1 -1
  332. package/lib/types/personal-skills/service.d.ts +6 -0
  333. package/lib/types/prompt-notes.d.ts +17 -4
  334. package/lib/types/qa-tools/docs-tools.d.ts +2 -2
  335. package/lib/types/request-queue.d.ts +32 -0
  336. package/lib/types/shared/settings.d.ts +19 -2
  337. package/lib/types/turn-failure-log.d.ts +18 -0
  338. package/lib/types/types.d.ts +200 -4
  339. package/package.json +64 -62
  340. package/scripts/attach-workspace-sessions.mjs +24 -5
  341. package/scripts/repair-session-events.mjs +25 -5
@@ -10,10 +10,25 @@ export interface CapabilityCatalogSnapshot {
10
10
  readonly skillIds: ReadonlySet<string>;
11
11
  /** Installed skills a person may invoke, including model-hidden ones. */
12
12
  readonly userSkillIds: ReadonlySet<string>;
13
+ /**
14
+ * The subset {@link userSkillIds} this account owns: what its own skills root
15
+ * contributes, never the deployment's shared root. The access model hands
16
+ * these to their owner without a role grant.
17
+ */
18
+ readonly ownUserSkillIds: ReadonlySet<string>;
13
19
  readonly skills: ReadonlyMap<string, SkillSummary>;
14
20
  /** Normalized `metadata.qa-surface` of every discovered skill. */
15
21
  readonly skillMetadata: ReadonlyMap<string, QaSkillDescriptor>;
16
22
  }
23
+ /**
24
+ * The viewing scope of one preset read, plus the release of the revision lease
25
+ * the preset registry hands it out with. The caller owns the lease for exactly
26
+ * as long as the read it borrowed the scope for.
27
+ */
28
+ export interface QaPresetScopeLease {
29
+ readonly key: ScopeKey;
30
+ release(): Promise<void>;
31
+ }
17
32
  /** Read-only adapter over the current DSH registries. */
18
33
  export declare class QaCapabilityCatalog {
19
34
  private readonly ctx;
@@ -42,12 +57,13 @@ export declare class QaCapabilityCatalog {
42
57
  * Resolving the scope may compose the preset; a failure degrades to the
43
58
  * global view instead of failing the page.
44
59
  */
45
- presetScope?: () => Promise<ScopeKey | undefined>);
60
+ presetScope?: () => Promise<QaPresetScopeLease | undefined>);
46
61
  snapshot(agent?: Agent): Promise<CapabilityCatalogSnapshot>;
62
+ private readCatalog;
47
63
  /**
48
- * The scope an administrator's catalog is read with. A deployment that pins
49
- * QA chats to an agent preset gets that preset's standing scope; without a
50
- * pinned preset there is no scope to borrow, and the read stays global.
64
+ * The scope lease an administrator's catalog is read with. A deployment that
65
+ * pins QA chats to an agent preset gets that preset's standing scope; without
66
+ * a pinned preset there is no scope to borrow, and the read stays global.
51
67
  */
52
68
  private resolvePresetScope;
53
69
  /**
@@ -28,6 +28,12 @@ export interface CapabilityAvailability {
28
28
  readonly skills: ReadonlySet<string>;
29
29
  /** Installed skills a person may invoke; defaults to the model-facing set. */
30
30
  readonly userSkills?: ReadonlySet<string>;
31
+ /**
32
+ * User-invocable skills the account of this session owns. They join
33
+ * `userSkills` for their owner whatever the role lists, because a personal
34
+ * skill belongs to one account and cannot be named in a shared role at all.
35
+ */
36
+ readonly ownSkills?: ReadonlySet<string>;
31
37
  }
32
38
  export interface ResolveCapabilityPolicyInput {
33
39
  readonly config: QaCapabilityConfig;
@@ -40,6 +46,23 @@ export interface ResolveCapabilityPolicyInput {
40
46
  /** Revision recorded with the session snapshot; opaque to enforcement. */
41
47
  readonly revision?: string;
42
48
  }
49
+ /**
50
+ * Names an administrator withdrew outright, which is every account at once,
51
+ * its owner included.
52
+ * @param config - the role configuration the withdrawal was recorded in.
53
+ */
54
+ export declare function withdrawnSkillNames(config: QaCapabilityConfig): ReadonlySet<string>;
55
+ /**
56
+ * The personal layer of one session's user-invoke list.
57
+ *
58
+ * Only an outright administrator withdrawal reaches a skill the account owns:
59
+ * the role overlay edits per-role audiences, and a personal skill is not a role
60
+ * grant, while `disabled` withdraws the name from the deployment for everyone,
61
+ * its owner included.
62
+ * @param config - the role configuration the policy is resolved against.
63
+ * @param ownSkills - the account's own user-invocable skills, if any.
64
+ */
65
+ export declare function personalUserSkillNames(config: QaCapabilityConfig, ownSkills: ReadonlySet<string> | undefined): readonly string[];
43
66
  export declare function resolveCapabilityPolicy({ config, subroleId, systemTools, systemSkills, available: installed, skillMetadata, revision, }: ResolveCapabilityPolicyInput): QaEffectiveCapabilityPolicy;
44
67
  export interface ResolveSkillAccessInput {
45
68
  readonly config: QaCapabilityConfig;
@@ -1,12 +1,11 @@
1
1
  import type { Agent } from "@deepseek-ai/dsh-agent";
2
2
  import type { Context } from "@deepseek-ai/cordis";
3
- import type { ScopeKey } from "@deepseek-ai/dsh-scope";
4
3
  import type { PluginLogger } from "@yadsh/dsh-plugin-log";
5
4
  import type { QaAccounts } from "../accounts/store.js";
6
5
  import type { QaAgentToolGrants } from "../enforcement/tool-grants.js";
7
6
  import type { QaAccessAdminSnapshot, QaAccountUserPublic, QaCapabilityConfig, QaCapabilityDescriptor, QaCapabilitySelection, QaClaimResult, QaCurrentAccess, QaEffectiveCapabilityPolicy, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, ResolvedQaSurfaceConfig } from "../types.js";
8
7
  import type { QaSessionLogReader } from "../admin/session-log.js";
9
- import { QaCapabilityCatalog, type CapabilityCatalogSnapshot } from "./capability-catalog.js";
8
+ import { QaCapabilityCatalog, type CapabilityCatalogSnapshot, type QaPresetScopeLease } from "./capability-catalog.js";
10
9
  import { QaRoleRepository } from "./role-repository.js";
11
10
  export interface QaResolvedSessionPolicy {
12
11
  readonly policy: QaEffectiveCapabilityPolicy;
@@ -51,7 +50,7 @@ export declare class QaAccessService {
51
50
  * preset's scope, so a global-only catalog left the operator unable to
52
51
  * see — or grant — what every QA chat actually mounts.
53
52
  */
54
- readonly presetScope?: () => Promise<ScopeKey | undefined>;
53
+ readonly presetScope?: () => Promise<QaPresetScopeLease | undefined>;
55
54
  /**
56
55
  * Durable session listing, used to tell a chat from a delegated child
57
56
  * across Host runs. Without it the lineage answer degrades to the live
@@ -1,6 +1,6 @@
1
1
  import type { PluginLogger } from "@yadsh/dsh-plugin-log";
2
2
  import { QaAccounts } from "./accounts/store.js";
3
- import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, QaWhoamiResult, ResolvedQaSurfaceConfig } from "./types.js";
3
+ import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, QaWhoamiResult, ResolvedQaSurfaceConfig } from "./types.js";
4
4
  /**
5
5
  * Host-side bodies of the account remotes the `QaSurface` service exposes.
6
6
  * The `@Remote`-decorated signatures stay on the service class (typert code
@@ -57,6 +57,13 @@ export interface QaAccountRemotes {
57
57
  updateProfile(token: string, input: QaAccountProfileInput): QaAccountUserPublic;
58
58
  /** Replace the caller's own starter buttons; the token is the identity. */
59
59
  updateStarters(token: string, input: QaAccountStartersInput): QaAccountUserPublic;
60
+ /**
61
+ * Replace the caller's own notification channels; the token is the identity.
62
+ * The deployment's own switches are not this write's business: they gate the
63
+ * channels at read time, so an account can only ever narrow what a stand
64
+ * allows, never widen it.
65
+ */
66
+ updateNotifications(token: string, input: QaAccountNotificationsInput): QaAccountUserPublic;
60
67
  /**
61
68
  * The caller's own integration tokens, newest last. Never a secret: the
62
69
  * plaintext exists only in the answer that minted it.
@@ -55,10 +55,20 @@ export declare class QaAccountsDatabase {
55
55
  get secret(): string;
56
56
  /** True while the in-memory model a caller holds is still current. */
57
57
  get unchangedSinceLoad(): boolean;
58
- /** Take the current data version as the baseline a caller's model reflects. */
59
- markLoaded(): void;
60
- /** Every account, ownership record and skill activation, in one read. */
58
+ /** Every account, ownership record and skill activation, as one snapshot. */
61
59
  loadAll(): AccountsFile;
60
+ /**
61
+ * The accounts model, read as the rows stand right now.
62
+ *
63
+ * Read through {@link loadAll}, never on its own: in autocommit every
64
+ * statement is a snapshot of its own, so these SELECTs are only one state
65
+ * when no other connection committed while they ran — which is exactly what
66
+ * the surrounding `PRAGMA data_version` check proves. The baseline used to be
67
+ * taken *after* the reads, which labeled a model of two states, and the token
68
+ * versions in it, as current: a token revoked by the `qa-accounts` CLI into
69
+ * this window kept working until some later write moved the version again.
70
+ */
71
+ private readModel;
62
72
  /** Append one account, preserving the order accounts are listed in. */
63
73
  insertUser(user: StoredUser): void;
64
74
  /** Replace one account's row in place. */
@@ -6,6 +6,8 @@
6
6
  export type QaAccountsErrorReason = "auth-required" | "admin-required" | "forbidden" | "invalid-credentials"
7
7
  /** The signed-in user typed a current password that does not match. */
8
8
  | "invalid-current-password" | "account-disabled" | "email-taken" | "invalid-email" | "invalid-display-name" | "invalid-profile" | "profile-disabled" | "invalid-starters" | "starters-disabled"
9
+ /** The notifications payload named a channel that is not a yes or a no. */
10
+ | "invalid-notifications"
9
11
  /** The endpoint this credential is for is switched off on this deployment. */
10
12
  | "integration-disabled" | "invalid-role" | "weak-password" | "registration-disabled" | "rate-limited" | "session-owned-elsewhere"
11
13
  /** The conversation a deletion names is not one this deployment knows. */
@@ -13,7 +15,13 @@ export type QaAccountsErrorReason = "auth-required" | "admin-required" | "forbid
13
15
  /** The Harness still holds the conversation open, so its log would come back. */
14
16
  | "conversation-live"
15
17
  /** This deployment cannot remove stored conversations at all. */
16
- | "conversation-not-removable";
18
+ | "conversation-not-removable"
19
+ /** The record an operator edited is no longer the one stored. */
20
+ | "memory-record-unknown"
21
+ /** Expert memory is not reachable: no plugin composed it, or its store is down. */
22
+ | "memory-unavailable"
23
+ /** The Host refused the memory edit itself, not who asked for it. */
24
+ | "invalid-memory";
17
25
  export declare class QaAccountsError extends Error {
18
26
  readonly reason: QaAccountsErrorReason;
19
27
  constructor(reason: QaAccountsErrorReason, message: string);
@@ -14,6 +14,16 @@ export interface StoredStarters {
14
14
  }[];
15
15
  hideDefaults?: boolean;
16
16
  }
17
+ /**
18
+ * One account's notification channels as they rest in the accounts store. Every
19
+ * field is optional because the record is read, not parsed: an account created
20
+ * before the settings form existed has none of it, and a hand-edited row must
21
+ * not decide anything the reader never chose.
22
+ */
23
+ export interface StoredNotifications {
24
+ inApp?: boolean;
25
+ desktop?: boolean;
26
+ }
17
27
  export interface StoredUser {
18
28
  readonly id: string;
19
29
  readonly email: string;
@@ -37,6 +47,8 @@ export interface StoredUser {
37
47
  profile?: StoredProfile;
38
48
  /** Absent until the owner customizes the starter buttons. */
39
49
  starters?: StoredStarters;
50
+ /** Absent until the owner opens the notifications form for the first time. */
51
+ notifications?: StoredNotifications;
40
52
  /** Agent capability profiles assigned by an administrator. */
41
53
  qaAccess?: QaUserAccess;
42
54
  }
@@ -1,4 +1,4 @@
1
- import type { QaAccountIdentityField, QaAccountProfile, QaAccountProfileInput, QaAccountRole, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaAccessUser, QaAdminAccountRow, QaClaimResult, QaEffectiveCapabilityPolicy, QaOwnershipEntry, QaIssuedServiceToken, QaPasswordResetRequest, QaServiceTokenSummary, QaSkillActivationRecord, QaUserAccess, QaWhoamiResult } from "../types.js";
1
+ import type { QaAccountIdentityField, QaAccountNotificationsInput, QaAccountProfile, QaAccountProfileInput, QaAccountRole, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaAccessUser, QaAdminAccountRow, QaClaimResult, QaEffectiveCapabilityPolicy, QaOwnershipEntry, QaIssuedServiceToken, QaPasswordResetRequest, QaServiceTokenSummary, QaSkillActivationRecord, QaUserAccess, QaWhoamiResult } from "../types.js";
2
2
  import { type StoredOwnership, type StoredUser } from "./file.js";
3
3
  import { type QaServiceTokenScope } from "./service-token.js";
4
4
  export { QaAccountsError } from "./errors.js";
@@ -105,6 +105,16 @@ export declare class QaAccounts {
105
105
  * mtime-and-size stamp of the JSON file used to provide.
106
106
  */
107
107
  private reloadIfChanged;
108
+ /**
109
+ * The authorization state as the database holds it right now.
110
+ *
111
+ * Every credential check reads through here, because what decides one — the
112
+ * token version and the disabled flag — is a row another process may have
113
+ * changed since this store last read. Refreshing inside the check rather than
114
+ * in whichever method the caller happened to invoke first is what keeps a
115
+ * revoked token from surviving in the in-memory model.
116
+ */
117
+ private authState;
108
118
  private pruneAuthAttempts;
109
119
  private assertAuthBudget;
110
120
  private userByEmail;
@@ -114,7 +124,14 @@ export declare class QaAccounts {
114
124
  /** Resolve a token to its user, or null for absent/invalid/expired ones. */
115
125
  verifyToken(token: string): string | null;
116
126
  private mintToken;
117
- /** The account behind a token, or an auth-required refusal. */
127
+ /**
128
+ * The account behind a token, or an auth-required refusal.
129
+ *
130
+ * The check reads the current authorization state, so the refusal lands on the
131
+ * call that presents a revoked token rather than on whichever method reloaded
132
+ * first. The personal-skill remotes gate on this call alone, which is what
133
+ * made the difference a security one.
134
+ */
118
135
  requireUser(token: string): StoredUser;
119
136
  /** Public identity behind a token; used by server-side access services. */
120
137
  currentUser(token: string): QaAccountUserPublic;
@@ -449,6 +466,17 @@ export declare class QaAccounts {
449
466
  * write leaves the stored record untouched.
450
467
  */
451
468
  updateOwnStarters(token: string, input: QaAccountStartersInput): QaAccountUserPublic;
469
+ /**
470
+ * Replace the token account's notification channels. Same self-service shape
471
+ * as {@link updateOwnStarters}: the token is the only identity, the write is
472
+ * full-replace, and a rejected payload leaves the stored record alone.
473
+ *
474
+ * Deliberately not gated by a config key the way profiles and starters are —
475
+ * a stand that wanted this switched off already has `notifications.enabled`,
476
+ * and two switches for one decision is how a deployment ends up with an
477
+ * answer nobody can find.
478
+ */
479
+ updateOwnNotifications(token: string, input: QaAccountNotificationsInput): QaAccountUserPublic;
452
480
  /**
453
481
  * Validate one profile write and stamp it onto the account. Throws before
454
482
  * anything is mutated, so a rejected write leaves the stored profile, and
@@ -19,6 +19,21 @@ export interface QaManualQueueEntry {
19
19
  readonly userId: string;
20
20
  readonly createdAt: string;
21
21
  }
22
+ /** The four record families the store keeps, one row each. */
23
+ export type QualityRowKind = "feedback" | "review" | "queue" | "audit";
24
+ /** How many rows of each family survive; the oldest fall off first. */
25
+ export declare const QA_ROW_CAPS: Readonly<Record<QualityRowKind, number>>;
26
+ /** Give up everything older in rank than the row at the cap, whose position the
27
+ * caller binds as `QA_ROW_CAPS[kind] - 1`. */
28
+ export declare const OVERFLOW_CUT_SQL = "\n DELETE FROM quality_rows\n WHERE kind = ?\n AND seq < (\n SELECT seq FROM quality_rows\n WHERE kind = ?\n ORDER BY seq DESC\n LIMIT 1 OFFSET ?\n )";
29
+ /** Read a whole family back in the order its records arrived. */
30
+ export declare const FAMILY_REPLAY_SQL = "\n SELECT json FROM quality_rows WHERE kind = ? ORDER BY seq";
31
+ /** Write one record at the end of its family's order, taking that end from the
32
+ * family's `MAX(seq)` whether the record is new or being re-judged. */
33
+ export declare const ROW_PLACEMENT_SQL = "\n INSERT INTO quality_rows (kind, key, seq, json)\n VALUES (?, ?, (SELECT COALESCE(MAX(seq), 0) + 1 FROM quality_rows WHERE kind = ?), ?)\n ON CONFLICT(kind, key) DO UPDATE SET\n seq = (SELECT COALESCE(MAX(seq), 0) + 1 FROM quality_rows WHERE kind = excluded.kind),\n json = excluded.json";
34
+ /** Read a whole family with each record's identity, the way the ownership sweep
35
+ * finds the rows of a conversation it is forgetting. */
36
+ export declare const FAMILY_MEMBERSHIP_SQL = "\n SELECT key, json FROM quality_rows WHERE kind = ?";
22
37
  export declare function defaultQualityFilePath(): string;
23
38
  /** The pre-SQLite quality file, imported once on first use. */
24
39
  export declare function defaultLegacyQualityFilePath(): string;
@@ -102,6 +117,24 @@ export declare class QaQualityStore {
102
117
  /** Append a record whose identity the store mints itself. */
103
118
  private appendRow;
104
119
  private deleteRow;
120
+ /**
121
+ * Drop the rows that overflow their family's cap.
122
+ *
123
+ * The rows to give up are the oldest by rank, not everything below
124
+ * `MAX(seq) - cap`. That range only equals the overflow while every sequence
125
+ * value is taken, and the store itself vacates values: an updated record
126
+ * moves to the end of the order and leaves its old value behind, and a
127
+ * dropped record leaves a hole. A range cut pays for each of those gaps with
128
+ * a real record, so a family at its cap loses one every time a record it
129
+ * already holds is re-rated — feedback disappears without anything new
130
+ * arriving to displace it.
131
+ *
132
+ * Reading that rank is not a seek: it walks cap entries of the family's own
133
+ * slice of the (kind, seq) index, so the cap bounds the cost of every write,
134
+ * while the delete reaches only the rows it removes and a write that does not
135
+ * overflow pays the read alone. Without the index the same read sorts the
136
+ * whole family into a temp B-tree.
137
+ */
105
138
  private applyCap;
106
139
  private rowsOf;
107
140
  private readDataVersion;
@@ -2,9 +2,10 @@ import type { PluginLogger } from "@yadsh/dsh-plugin-log";
2
2
  import type { QaAccounts } from "../accounts/store.js";
3
3
  import type { QaAccessService } from "../access/service.js";
4
4
  import type { QaRoleRepository } from "../access/role-repository.js";
5
- import type { QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminSkillScope, QaAdminSkillsView, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaPasswordResetRequest, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillToolDescriptor, QaSkillValidation, QaUserQuery } from "../types.js";
5
+ import type { QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminSkillScope, QaAdminSkillsView, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaExpertMemoryDraft, QaExpertMemoryPage, QaExpertMemoryRecord, QaExpertMemoryScope, QaFeedbackHarvestEntry, QaFeedbackHarvestResult, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaPasswordResetRequest, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillToolDescriptor, QaSkillValidation, QaUserQuery } from "../types.js";
6
6
  import type { QaPersonalSkills } from "../personal-skills/index.js";
7
- import type { QaQualityStore } from "./quality-store.js";
7
+ import type { QaDomainExpertsFace } from "../integration/expert-memory.js";
8
+ import { type QaQualityStore } from "./quality-store.js";
8
9
  import type { QaAdminRedactor } from "./redaction.js";
9
10
  import type { QaSessionLogReader, QaStoredSessionEraser } from "./session-log.js";
10
11
  /**
@@ -43,6 +44,14 @@ export interface QaAdminServiceOptions {
43
44
  * never constructs storage before someone opens its skills page.
44
45
  */
45
46
  readonly skills: () => QaPersonalSkills;
47
+ /**
48
+ * The domain-experts service, if this deployment composed one. Resolved
49
+ * lazily for the same reason `skills` is, and for one more: a stand without
50
+ * experts must still open its console, so the memory page learns "there is
51
+ * nothing here to maintain" from this returning `undefined` rather than from a
52
+ * missing import at boot.
53
+ */
54
+ readonly domainExperts?: () => QaDomainExpertsFace | undefined;
46
55
  /** Drops the sources a conversation collected, when the deployment keeps any. */
47
56
  readonly dropSources?: (sessionId: string) => void;
48
57
  readonly logger: PluginLogger;
@@ -52,6 +61,8 @@ export interface QaAdminServiceOptions {
52
61
  export declare class QaAdminService {
53
62
  private readonly options;
54
63
  private readonly redactor;
64
+ /** The aggregate pass running now, shared by the callers waiting for it. */
65
+ private scanning;
55
66
  private readonly transcripts;
56
67
  constructor(options: QaAdminServiceOptions);
57
68
  private requireAccounts;
@@ -74,11 +85,46 @@ export declare class QaAdminService {
74
85
  * costs a listing per interval at most.
75
86
  */
76
87
  private index;
77
- /** One conversation's projected log, cached for the TTL unless asked fresh. */
88
+ /**
89
+ * One conversation's projected log, held across calls.
90
+ *
91
+ * A log the Harness no longer holds is finished — nothing can append to it —
92
+ * so its projection stays valid until the cache evicts it or the conversation
93
+ * is deleted. Only a log this process is still writing is re-read once the TTL
94
+ * expires. A cache hit counts as a use: an aggregate pass walks the whole
95
+ * window, and evicting by read time would drop the conversations the pages ask
96
+ * for most often and then read them again.
97
+ *
98
+ * The one answer this can hold back is a conversation resumed and finished
99
+ * between two page loads: its counters lag until the log is read for another
100
+ * reason. The conversation itself is never affected — the detail view always
101
+ * reads the log — and what the aggregate pages show instead is the newest
102
+ * projection of every finished conversation, which is the cheaper lie.
103
+ */
78
104
  private transcript;
79
- private facts;
80
- overview(token: string): Promise<QaAdminOverview>;
81
- metrics(token: string): Promise<QaQualityMetrics>;
105
+ /** Insert or re-touch one projection, keeping the cache inside its budget. */
106
+ private retain;
107
+ /**
108
+ * The aggregate rows of the newest scan window, read in one pass.
109
+ *
110
+ * The message counts and the tool failures come from the same projections, so
111
+ * one walk fills both. While a walk runs, a caller that wants the same window
112
+ * joins it rather than starting a second scan of the deployment's logs, and the
113
+ * walk stops once no caller waits for it: a scan nobody is reading is only ever
114
+ * a load on the store.
115
+ */
116
+ private scanOf;
117
+ /**
118
+ * Walk one scan window, filling the counts and the failure signals as it goes.
119
+ *
120
+ * `waiting` is the caller set of the pass this walk belongs to: the walk gives
121
+ * up between two reads once every caller has left, so a reviewer who abandons a
122
+ * page that has not answered does not keep the deployment reading logs for an
123
+ * answer nobody will see.
124
+ */
125
+ private walk;
126
+ overview(token: string, signal?: AbortSignal): Promise<QaAdminOverview>;
127
+ metrics(token: string, signal?: AbortSignal): Promise<QaQualityMetrics>;
82
128
  private metricsOf;
83
129
  /** One attention line per condition worth an administrator's time. */
84
130
  private alertsOf;
@@ -108,7 +154,7 @@ export declare class QaAdminService {
108
154
  private assertAdminSurvives;
109
155
  /** Validate an assignment against the enabled roles before storing it. */
110
156
  private exactAssignment;
111
- conversations(token: string, query: QaConversationQuery, cursor: string | undefined, limit: number | undefined): Promise<QaAdminPage<QaConversationSummary>>;
157
+ conversations(token: string, query: QaConversationQuery, cursor: string | undefined, limit: number | undefined, signal?: AbortSignal): Promise<QaAdminPage<QaConversationSummary>>;
112
158
  private summaryOf;
113
159
  conversation(token: string, conversationId: string): Promise<QaConversationDetail>;
114
160
  /**
@@ -130,17 +176,31 @@ export declare class QaAdminService {
130
176
  */
131
177
  deleteConversation(token: string, conversationId: string): Promise<QaConversationDeletion>;
132
178
  private failuresOf;
133
- private failureSignals;
134
179
  /**
135
180
  * Record the caller's verdict on one assistant message. The conversation must
136
181
  * be the caller's own: a rating is the user's own signal, and nobody rates
137
182
  * someone else's answer on their behalf.
138
183
  */
139
184
  rateMessage(token: string, conversationId: string, messageId: string, input: QaMessageFeedbackInput): QaMessageFeedback;
185
+ /**
186
+ * Replay the ratings a browser still holds from the window in which a thumbs
187
+ * never reached the Host.
188
+ *
189
+ * A harvest never overwrites. The browser's map stores no timestamp, so an
190
+ * entry may well be a rating the same user has since filed together with a
191
+ * reason and a comment; replacing that record with the bare thumbs would
192
+ * delete exactly what a reviewer reads. An entry the Host already answers for
193
+ * is counted as present and left alone.
194
+ *
195
+ * One unusable or foreign entry costs that entry alone: a browser keeps
196
+ * ratings of chats it no longer owns, and the rest of the replay must not be
197
+ * lost because of them.
198
+ */
199
+ harvestFeedback(token: string, entries: readonly QaFeedbackHarvestEntry[]): QaFeedbackHarvestResult;
140
200
  feedback(token: string, query: QaFeedbackQuery, cursor: string | undefined, limit: number | undefined): Promise<QaAdminPage<QaFeedbackRow>>;
141
201
  private feedbackRows;
142
202
  private queueRows;
143
- reviewQueue(token: string, cursor: string | undefined, limit: number | undefined): Promise<QaAdminPage<QaReviewQueueRow>>;
203
+ reviewQueue(token: string, cursor: string | undefined, limit: number | undefined, signal?: AbortSignal): Promise<QaAdminPage<QaReviewQueueRow>>;
144
204
  /** Park a conversation for review without rating it. */
145
205
  queueConversation(token: string, conversationId: string, messageId: string | undefined): QaReviewQueueItem;
146
206
  /** Save a reviewer's verdict, and take the item out of the queue when done. */
@@ -179,6 +239,56 @@ export declare class QaAdminService {
179
239
  removeSkill(token: string, scope: QaAdminSkillScope, name: string, expectedRevision: string | null): Promise<QaSkillRemoval>;
180
240
  /** One skill as it exists now; a missing one is the service's refusal. */
181
241
  private existingSkill;
242
+ /**
243
+ * The domain-experts maintenance seam, or the refusal that says this stand
244
+ * runs no experts.
245
+ *
246
+ * Reach is host-plane on purpose: the sibling plugin's remotes carry no token
247
+ * and check no permission, because inside the Harness their caller is already
248
+ * a trusted component. This console answers a browser on a LAN port, so it
249
+ * calls the service directly and gates every call with its own `require`.
250
+ */
251
+ private expertMemory;
252
+ /**
253
+ * Run one memory call, translating the sibling plugin's refusal.
254
+ *
255
+ * The reason crosses the wire, never the message: a memory refusal names
256
+ * namespaces, and a storage failure names the database file behind them.
257
+ */
258
+ private memoryCall;
259
+ /**
260
+ * What an audit row keeps about one record.
261
+ *
262
+ * A remembered line runs to a few pages, and the trail holds a capped JSON
263
+ * snapshot; the identity of the record and the head of its text is what an
264
+ * operator reads back to decide whether the deletion was wrong.
265
+ */
266
+ private memoryImage;
267
+ /** The record one key holds now, if it still holds one. */
268
+ private rememberedRecord;
269
+ /** Every expert memory namespace on the stand, with its owner and size. */
270
+ memoryScopes(token: string): Promise<readonly QaExpertMemoryScope[]>;
271
+ /** One page of one namespace, newest first, with the filter's match count. */
272
+ memoryRecords(token: string, namespace: string, query: string, limit: number | null, offset: number): Promise<QaExpertMemoryPage>;
273
+ /**
274
+ * Rewrite one remembered line.
275
+ *
276
+ * The key is not editable: it is what the expert's own notes and the audit
277
+ * trail point at, and a console that renamed it would orphan both. The write
278
+ * refuses a record that vanished meanwhile rather than re-creating it — an
279
+ * operator editing a stale row should reload, not resurrect.
280
+ */
281
+ correctMemory(token: string, namespace: string, key: string, draft: QaExpertMemoryDraft): Promise<QaExpertMemoryRecord>;
282
+ /**
283
+ * Drop one or several remembered lines.
284
+ *
285
+ * A single deletion keeps the text it removed; a bulk one keeps only the
286
+ * count and the keys, because an audit trail that copies every page of memory
287
+ * it deletes becomes the thing an operator has to clean up.
288
+ */
289
+ forgetMemory(token: string, namespace: string, keys: readonly string[]): Promise<number>;
290
+ /** Empty one expert's private namespace. */
291
+ wipeMemory(token: string, namespace: string, expectedRecords: number | null): Promise<number>;
182
292
  /**
183
293
  * The audit trail as one table. Two writers keep records — the capability
184
294
  * service, whose repository predates the console, and the quality store —
@@ -1,4 +1,4 @@
1
- import type { QaAccountProfileInput, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, ResolvedQaSurfaceConfig } from "../types.js";
1
+ import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, ResolvedQaSurfaceConfig } from "../types.js";
2
2
  import type { QaAccountsApi, QaIntegrationTokenResult, StorageLike } from "./types.js";
3
3
  /**
4
4
  * What the sign-in card says after a forgotten-password request. It names the
@@ -23,12 +23,19 @@ export type QaAccountsSnapshot = {
23
23
  * plus every mapped chat for an admin (the cross-user grouping view).
24
24
  */
25
25
  readonly ownedIds: readonly string[];
26
+ /**
27
+ * The account's own chats alone — {@link ownedIds} without the
28
+ * cross-user view. Reading another account's chat is not a claim on its
29
+ * activity, so a surface that reports a chat's state outward is bounded
30
+ * by this list rather than by the sidebar's.
31
+ */
32
+ readonly ownIds: readonly string[];
26
33
  /**
27
34
  * The full ownership map; admins only, empty for ordinary accounts and
28
35
  * whenever the admin listing was refused or unavailable.
29
36
  */
30
37
  readonly ownership: readonly QaOwnershipEntry[];
31
- /** Bumped whenever ownedIds or ownership changes; re-projects lists. */
38
+ /** Bumped whenever ownedIds, ownIds or ownership changes; re-projects lists. */
32
39
  readonly ownedRevision: number;
33
40
  };
34
41
  /**
@@ -49,6 +56,17 @@ export interface QaAccountsControllerOptions {
49
56
  */
50
57
  readonly legacyChatIds?: () => readonly string[];
51
58
  readonly forgetChat?: (sessionId: string) => void;
59
+ /**
60
+ * Replay this browser's locally held ratings onto the account it just signed
61
+ * in as, for the chats that account owns. Runs once per account: the surface
62
+ * is usable while it is in flight, and a failure costs only the retry the
63
+ * next login performs.
64
+ */
65
+ readonly harvestRatings?: (context: {
66
+ readonly token: string;
67
+ readonly accountId: string;
68
+ readonly ownedIds: readonly string[];
69
+ }) => Promise<void>;
52
70
  }
53
71
  /**
54
72
  * Browser-side account lifecycle: token persistence, the checking → gate →
@@ -71,6 +89,12 @@ export declare class QaAccountsController {
71
89
  token(): string | null;
72
90
  /** Server-owned chat ids (empty until authed); falls back to nothing. */
73
91
  ownedIds(): readonly string[];
92
+ /**
93
+ * The chat ids this account owns outright (empty until authed): the set the
94
+ * admin's cross-user view may be wider than, and the scope a turn notice
95
+ * speaks within.
96
+ */
97
+ ownIds(): readonly string[];
74
98
  /** Boot probe: restore the stored token and ask the Host who it is. */
75
99
  start(): Promise<void>;
76
100
  login(email: string, password: string): Promise<void>;
@@ -112,6 +136,9 @@ export declare class QaAccountsController {
112
136
  * created or opened after login enters it here — the next login is not a
113
137
  * reasonable price for a chat the visitor is looking at. Ids already known,
114
138
  * and the ones an admin's cross-user view contributed, are left as they are.
139
+ *
140
+ * A chat this page claimed is owned outright, so both lists gain it: the
141
+ * sidebar's wider read is not what decides that.
115
142
  */
116
143
  private noteOwnedSession;
117
144
  /**
@@ -127,6 +154,13 @@ export declare class QaAccountsController {
127
154
  * carries the stored record, so the composer picks the new buttons up.
128
155
  */
129
156
  updateStarters(input: QaAccountStartersInput): Promise<string | null>;
157
+ /**
158
+ * Replace the signed-in user's own notification channels, mirroring
159
+ * {@link updateStarters}: refusal copy on rejection, null once the snapshot
160
+ * carries the stored record — which is also how the page learns the choice
161
+ * took effect, since it reads the same snapshot the notices plan with.
162
+ */
163
+ updateNotifications(input: QaAccountNotificationsInput): Promise<string | null>;
130
164
  /**
131
165
  * The signed-in user's integration tokens. Resolves to the list, or to the
132
166
  * copy the page renders when the Host refused the read; the tokens are not
@@ -145,7 +179,7 @@ export declare class QaAccountsController {
145
179
  */
146
180
  revokeServiceToken(tokenId: string): Promise<QaIntegrationTokenResult<null>>;
147
181
  private authenticate;
148
- /** Own the account, migrate the legacy index, then project authed. */
182
+ /** Own the account, migrate the legacy index, replay held ratings, then auth. */
149
183
  private enterSession;
150
184
  /** Refresh the owned list after index changes; cheap enough to re-pull. */
151
185
  refreshOwned(): Promise<void>;
@@ -1,4 +1,4 @@
1
- import type { SettingsScope } from "@deepseek-ai/dsh-client-ui-settings/client";
1
+ import type { ConfigForm } from "@deepseek-ai/dsh-client-ui-settings/client";
2
2
  import type { QaSurfaceConfig, ResolvedQaSurfaceConfig } from "../types.js";
3
3
  export interface QaConfigSnapshot {
4
4
  readonly status: "loading" | "ready" | "unavailable" | "error";
@@ -26,7 +26,7 @@ export declare class QaConfigController {
26
26
  private fallbackState;
27
27
  private fallbackConfig;
28
28
  private disposed;
29
- constructor(scope: SettingsScope<QaSurfaceConfig>, fallback?: QaConfigFallback);
29
+ constructor(scope: ConfigForm<QaSurfaceConfig>, fallback?: QaConfigFallback);
30
30
  getSnapshot: () => QaConfigSnapshot;
31
31
  subscribe: (listener: () => void) => (() => void);
32
32
  dispose(): void;