@yadsh/dsh-qa-surface 0.9.0 → 0.11.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 (250) hide show
  1. package/README.md +269 -2
  2. package/lib/access/service.js +49 -10
  3. package/lib/access/service.js.map +1 -1
  4. package/lib/account-remotes.js +41 -0
  5. package/lib/account-remotes.js.map +1 -1
  6. package/lib/accounts/database.js +140 -0
  7. package/lib/accounts/database.js.map +1 -1
  8. package/lib/accounts/errors.js.map +1 -1
  9. package/lib/accounts/service-token.js +103 -0
  10. package/lib/accounts/service-token.js.map +1 -0
  11. package/lib/accounts/store.js +291 -5
  12. package/lib/accounts/store.js.map +1 -1
  13. package/lib/admin/conversation-log.js +14 -8
  14. package/lib/admin/conversation-log.js.map +1 -1
  15. package/lib/admin/permissions.js +5 -0
  16. package/lib/admin/permissions.js.map +1 -1
  17. package/lib/admin/quality-store.js +4 -0
  18. package/lib/admin/quality-store.js.map +1 -1
  19. package/lib/admin/service.js +180 -7
  20. package/lib/admin/service.js.map +1 -1
  21. package/lib/admin/session-log.js +39 -8
  22. package/lib/admin/session-log.js.map +1 -1
  23. package/lib/attachment-rules.js +39 -6
  24. package/lib/attachment-rules.js.map +1 -1
  25. package/lib/cli.js +83 -1
  26. package/lib/cli.js.map +1 -1
  27. package/lib/client/QaAccountsController.js +213 -4
  28. package/lib/client/QaAccountsController.js.map +1 -1
  29. package/lib/client/QaSessionController.js +49 -21
  30. package/lib/client/QaSessionController.js.map +1 -1
  31. package/lib/client/QaSurface.js +51 -9
  32. package/lib/client/QaSurface.js.map +1 -1
  33. package/lib/client/QaSurfaceGuard.js +12 -5
  34. package/lib/client/QaSurfaceGuard.js.map +1 -1
  35. package/lib/client/admin/QaAdmin.js +14 -2
  36. package/lib/client/admin/QaAdmin.js.map +1 -1
  37. package/lib/client/admin/pages/SkillFiles.js +78 -0
  38. package/lib/client/admin/pages/SkillFiles.js.map +1 -0
  39. package/lib/client/admin/pages/Users.js +74 -3
  40. package/lib/client/admin/pages/Users.js.map +1 -1
  41. package/lib/client/admin/routes.js +8 -1
  42. package/lib/client/admin/routes.js.map +1 -1
  43. package/lib/client/admin/shared.js +5 -1
  44. package/lib/client/admin/shared.js.map +1 -1
  45. package/lib/client/admin/styles.js +2 -0
  46. package/lib/client/admin/styles.js.map +1 -1
  47. package/lib/client/attachments.js +41 -11
  48. package/lib/client/attachments.js.map +1 -1
  49. package/lib/client/base64.js +22 -1
  50. package/lib/client/base64.js.map +1 -1
  51. package/lib/client/components/QaAgentsDrawer.js +9 -6
  52. package/lib/client/components/QaAgentsDrawer.js.map +1 -1
  53. package/lib/client/components/QaApproval.js +1 -1
  54. package/lib/client/components/QaApproval.js.map +1 -1
  55. package/lib/client/components/QaAuthGate.js +19 -10
  56. package/lib/client/components/QaAuthGate.js.map +1 -1
  57. package/lib/client/components/QaChangelog.js +56 -0
  58. package/lib/client/components/QaChangelog.js.map +1 -1
  59. package/lib/client/components/QaFilesPanel.js +17 -11
  60. package/lib/client/components/QaFilesPanel.js.map +1 -1
  61. package/lib/client/components/QaHeader.js +3 -11
  62. package/lib/client/components/QaHeader.js.map +1 -1
  63. package/lib/client/components/QaModal.js.map +1 -1
  64. package/lib/client/components/QaQuestions.js +4 -4
  65. package/lib/client/components/QaQuestions.js.map +1 -1
  66. package/lib/client/components/QaSidebar.js +10 -2
  67. package/lib/client/components/QaSidebar.js.map +1 -1
  68. package/lib/client/components/QaWidthHandle.js +111 -5
  69. package/lib/client/components/QaWidthHandle.js.map +1 -1
  70. package/lib/client/components/QaWorkspaceBrowser.js +231 -0
  71. package/lib/client/components/QaWorkspaceBrowser.js.map +1 -0
  72. package/lib/client/index.js +122 -53
  73. package/lib/client/index.js.map +1 -1
  74. package/lib/client/kiosk.js +51 -0
  75. package/lib/client/kiosk.js.map +1 -0
  76. package/lib/client/lineage.js +64 -0
  77. package/lib/client/lineage.js.map +1 -1
  78. package/lib/client/markdown/blocks.js +122 -7
  79. package/lib/client/markdown/blocks.js.map +1 -1
  80. package/lib/client/markdown/inline.js +108 -12
  81. package/lib/client/markdown/inline.js.map +1 -1
  82. package/lib/client/markdown/katex-css.js +12 -0
  83. package/lib/client/markdown/katex-css.js.map +1 -0
  84. package/lib/client/markdown/math.js +84 -0
  85. package/lib/client/markdown/math.js.map +1 -0
  86. package/lib/client/markdown/render.js +74 -5
  87. package/lib/client/markdown/render.js.map +1 -1
  88. package/lib/client/settings/card.js +2 -2
  89. package/lib/client/settings/card.js.map +1 -1
  90. package/lib/client/settings/sections/attachments.js +6 -5
  91. package/lib/client/settings/sections/attachments.js.map +1 -1
  92. package/lib/client/settings/sections/notes.js +64 -0
  93. package/lib/client/settings/sections/notes.js.map +1 -0
  94. package/lib/client/settings/sections/sources.js +4 -1
  95. package/lib/client/settings/sections/sources.js.map +1 -1
  96. package/lib/client/settings/sections.js +1 -0
  97. package/lib/client/settings/sections.js.map +1 -1
  98. package/lib/client/settlement.js +1 -1
  99. package/lib/client/settlement.js.map +1 -1
  100. package/lib/client/source-preview.js +3 -0
  101. package/lib/client/source-preview.js.map +1 -1
  102. package/lib/client/styles.js +106 -18
  103. package/lib/client/styles.js.map +1 -1
  104. package/lib/client/types.js.map +1 -1
  105. package/lib/client/user-settings/IntegrationTokensPage.js +150 -0
  106. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -0
  107. package/lib/client/user-settings/PasswordSettingsPage.js +48 -0
  108. package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -0
  109. package/lib/client/user-settings/SkillCatalog.js +2 -2
  110. package/lib/client/user-settings/SkillCatalog.js.map +1 -1
  111. package/lib/client/user-settings/UserSettingsDialog.js +17 -2
  112. package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
  113. package/lib/client/user-settings/copy.js +20 -0
  114. package/lib/client/user-settings/copy.js.map +1 -1
  115. package/lib/client.js +31007 -686
  116. package/lib/client.js.map +1 -1
  117. package/lib/config-resolvers/attachments.js +6 -5
  118. package/lib/config-resolvers/attachments.js.map +1 -1
  119. package/lib/config-resolvers/defaults.js +33 -2
  120. package/lib/config-resolvers/defaults.js.map +1 -1
  121. package/lib/config-resolvers/integration.js +94 -0
  122. package/lib/config-resolvers/integration.js.map +1 -0
  123. package/lib/config-resolvers/notes.js +26 -0
  124. package/lib/config-resolvers/notes.js.map +1 -0
  125. package/lib/config-resolvers/sources.js +4 -0
  126. package/lib/config-resolvers/sources.js.map +1 -1
  127. package/lib/config.js +107 -0
  128. package/lib/config.js.map +1 -1
  129. package/lib/index.js +348 -12
  130. package/lib/index.js.map +1 -1
  131. package/lib/integration/answer.js +100 -0
  132. package/lib/integration/answer.js.map +1 -0
  133. package/lib/integration/attachments.js +231 -0
  134. package/lib/integration/attachments.js.map +1 -0
  135. package/lib/integration/contract.js +53 -0
  136. package/lib/integration/contract.js.map +1 -0
  137. package/lib/integration/host-runner.js +243 -0
  138. package/lib/integration/host-runner.js.map +1 -0
  139. package/lib/integration/http.js +538 -0
  140. package/lib/integration/http.js.map +1 -0
  141. package/lib/integration/service.js +331 -0
  142. package/lib/integration/service.js.map +1 -0
  143. package/lib/integration/transcript-reader.js +202 -0
  144. package/lib/integration/transcript-reader.js.map +1 -0
  145. package/lib/integration/transcript.js +148 -0
  146. package/lib/integration/transcript.js.map +1 -0
  147. package/lib/personal-skills/index.js +2 -2
  148. package/lib/personal-skills/index.js.map +1 -1
  149. package/lib/personal-skills/paths.js +14 -8
  150. package/lib/personal-skills/paths.js.map +1 -1
  151. package/lib/personal-skills/provider.js +16 -4
  152. package/lib/personal-skills/provider.js.map +1 -1
  153. package/lib/personal-skills/service.js +240 -30
  154. package/lib/personal-skills/service.js.map +1 -1
  155. package/lib/prompt-notes.js +103 -6
  156. package/lib/prompt-notes.js.map +1 -1
  157. package/lib/provenance/document-preview.js +60 -0
  158. package/lib/provenance/document-preview.js.map +1 -0
  159. package/lib/provenance/file-preview.js +191 -19
  160. package/lib/provenance/file-preview.js.map +1 -1
  161. package/lib/qa-tools/catalog.js +18 -1
  162. package/lib/qa-tools/catalog.js.map +1 -1
  163. package/lib/qa-tools/docs-tools.js +563 -0
  164. package/lib/qa-tools/docs-tools.js.map +1 -0
  165. package/lib/qa-tools/file-delete-gate.js +60 -0
  166. package/lib/qa-tools/file-delete-gate.js.map +1 -0
  167. package/lib/qa-tools/file-delete.js +156 -0
  168. package/lib/qa-tools/file-delete.js.map +1 -0
  169. package/lib/resolve-config.js +6 -0
  170. package/lib/resolve-config.js.map +1 -1
  171. package/lib/secure-session.js +83 -3
  172. package/lib/secure-session.js.map +1 -1
  173. package/lib/shared/documents.js +22 -0
  174. package/lib/shared/documents.js.map +1 -0
  175. package/lib/shared/integration-tokens.js +23 -0
  176. package/lib/shared/integration-tokens.js.map +1 -0
  177. package/lib/typert.host.js +1320 -134
  178. package/lib/typert.remote-client.d.ts +33 -1
  179. package/lib/typert.remote-client.js +1102 -74
  180. package/lib/types/access/service.d.ts +33 -2
  181. package/lib/types/account-remotes.d.ts +30 -1
  182. package/lib/types/accounts/database.d.ts +53 -0
  183. package/lib/types/accounts/errors.d.ts +5 -1
  184. package/lib/types/accounts/service-token.d.ts +70 -0
  185. package/lib/types/accounts/store.d.ts +131 -1
  186. package/lib/types/admin/conversation-log.d.ts +12 -0
  187. package/lib/types/admin/service.d.ts +56 -1
  188. package/lib/types/admin/session-log.d.ts +21 -0
  189. package/lib/types/attachment-rules.d.ts +26 -3
  190. package/lib/types/client/QaAccountsController.d.ts +64 -5
  191. package/lib/types/client/QaSessionController.d.ts +24 -2
  192. package/lib/types/client/admin/pages/SkillFiles.d.ts +17 -0
  193. package/lib/types/client/admin/routes.d.ts +2 -0
  194. package/lib/types/client/admin/shared.d.ts +2 -0
  195. package/lib/types/client/attachments.d.ts +21 -4
  196. package/lib/types/client/base64.d.ts +13 -1
  197. package/lib/types/client/components/QaAgentsDrawer.d.ts +2 -1
  198. package/lib/types/client/components/QaFilesPanel.d.ts +16 -4
  199. package/lib/types/client/components/QaHeader.d.ts +9 -3
  200. package/lib/types/client/components/QaModal.d.ts +4 -3
  201. package/lib/types/client/components/QaWidthHandle.d.ts +48 -2
  202. package/lib/types/client/components/QaWorkspaceBrowser.d.ts +15 -0
  203. package/lib/types/client/index.d.ts +1 -1
  204. package/lib/types/client/kiosk.d.ts +34 -0
  205. package/lib/types/client/lineage.d.ts +42 -0
  206. package/lib/types/client/markdown/blocks.d.ts +14 -1
  207. package/lib/types/client/markdown/inline.d.ts +23 -7
  208. package/lib/types/client/markdown/katex-css.d.ts +11 -0
  209. package/lib/types/client/markdown/math.d.ts +23 -0
  210. package/lib/types/client/markdown/render.d.ts +2 -1
  211. package/lib/types/client/settings/sections/notes.d.ts +12 -0
  212. package/lib/types/client/settings/sections.d.ts +1 -0
  213. package/lib/types/client/settlement.d.ts +4 -1
  214. package/lib/types/client/styles.d.ts +15 -2
  215. package/lib/types/client/types.d.ts +120 -1
  216. package/lib/types/client/user-settings/IntegrationTokensPage.d.ts +21 -0
  217. package/lib/types/client/user-settings/PasswordSettingsPage.d.ts +19 -0
  218. package/lib/types/client/user-settings/UserSettingsDialog.d.ts +11 -2
  219. package/lib/types/client/user-settings/copy.d.ts +10 -1
  220. package/lib/types/config-resolvers/integration.d.ts +47 -0
  221. package/lib/types/config-resolvers/notes.d.ts +10 -0
  222. package/lib/types/config.d.ts +10 -0
  223. package/lib/types/index.d.ts +111 -1
  224. package/lib/types/integration/answer.d.ts +65 -0
  225. package/lib/types/integration/attachments.d.ts +85 -0
  226. package/lib/types/integration/contract.d.ts +196 -0
  227. package/lib/types/integration/host-runner.d.ts +44 -0
  228. package/lib/types/integration/http.d.ts +79 -0
  229. package/lib/types/integration/service.d.ts +120 -0
  230. package/lib/types/integration/transcript-reader.d.ts +100 -0
  231. package/lib/types/integration/transcript.d.ts +59 -0
  232. package/lib/types/personal-skills/index.d.ts +2 -2
  233. package/lib/types/personal-skills/paths.d.ts +15 -3
  234. package/lib/types/personal-skills/provider.d.ts +14 -2
  235. package/lib/types/personal-skills/service.d.ts +89 -14
  236. package/lib/types/prompt-notes.d.ts +37 -0
  237. package/lib/types/provenance/document-preview.d.ts +28 -0
  238. package/lib/types/provenance/file-preview.d.ts +69 -2
  239. package/lib/types/qa-tools/catalog.d.ts +1 -1
  240. package/lib/types/qa-tools/docs-tools.d.ts +160 -0
  241. package/lib/types/qa-tools/file-delete-gate.d.ts +42 -0
  242. package/lib/types/qa-tools/file-delete.d.ts +32 -0
  243. package/lib/types/secure-session.d.ts +52 -1
  244. package/lib/types/shared/documents.d.ts +20 -0
  245. package/lib/types/shared/integration-tokens.d.ts +24 -0
  246. package/lib/types/types.d.ts +263 -3
  247. package/lib/types/ui-mode.d.ts +18 -0
  248. package/lib/ui-mode.js +22 -0
  249. package/lib/ui-mode.js.map +1 -0
  250. package/package.json +12 -10
package/lib/index.js CHANGED
@@ -37,7 +37,7 @@ import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
37
37
  import { SessionId } from "@deepseek-ai/dsh-session/types";
38
38
  import { WorkspaceId } from "@deepseek-ai/dsh-workspace";
39
39
  import { createHostLoggerSink, getPluginLogger, } from "@yadsh/dsh-plugin-log";
40
- import { ConfigSchema, resolveConfig } from "./config.js";
40
+ import { ConfigSchema, qaSurfaceVersion, resolveConfig } from "./config.js";
41
41
  import { createQaAccountRemotes } from "./account-remotes.js";
42
42
  import { QaAdminService } from "./admin/service.js";
43
43
  import { QaQualityStore } from "./admin/quality-store.js";
@@ -45,12 +45,17 @@ import { createSessionEraser, createSessionLogReader, } from "./admin/session-lo
45
45
  import { createQaPersonalSkillRemotes, QaPersonalSkillsHost, } from "./personal-skills/index.js";
46
46
  import { QaApprovalGate } from "./approvals.js";
47
47
  import { QaAttestationError, qaAttestationFailureMessage, } from "./attestation.js";
48
+ import { QaFileDeleteGate } from "./qa-tools/file-delete-gate.js";
48
49
  import { QaQuestionGate } from "./questions.js";
49
50
  import { QaSessionOwnership } from "./session-ownership.js";
50
51
  import { entryRedirectRow } from "./entry-redirect.js";
52
+ import { qaKioskDeployment } from "./ui-mode.js";
51
53
  import { registerQaNavigationRoute } from "./host-route.js";
52
54
  import { makeLaunchTokenSource } from "./launch-token.js";
53
55
  import { QaIntegrationPrincipalBindings } from "./integration-principals.js";
56
+ import { registerQaIntegrationRoutes } from "./integration/http.js";
57
+ import { createQaIntegrationRunner } from "./integration/host-runner.js";
58
+ import { QaIntegrationService } from "./integration/service.js";
54
59
  import { QaPolicyAdmission } from "./secure-session.js";
55
60
  import { QaAccessService } from "./access/service.js";
56
61
  import { createQaSlashRemotes } from "./slash/remotes.js";
@@ -58,7 +63,8 @@ import { QaPromptNotes } from "./prompt-notes.js";
58
63
  import { QaTools } from "./qa-tools/index.js";
59
64
  import { QaProvenanceHost } from "./provenance/host-store.js";
60
65
  import { defaultLegacyProvenanceFilePath, defaultQaProvenanceDirectoryPath, FileQaProvenanceSnapshotStore, } from "./provenance/snapshot-store.js";
61
- import { readSourceFilePreview, QaSourcePreviewError, } from "./provenance/file-preview.js";
66
+ import { previewConvertibleDocument } from "./provenance/document-preview.js";
67
+ import { listWorkspaceDirectory, readSourceFilePreview, readWorkspaceFile, QaSourcePreviewError, } from "./provenance/file-preview.js";
62
68
  import { existingQaUserWorkspace, prepareQaUserWorkspace, } from "./user-workspace.js";
63
69
  export const name = "qa-surface";
64
70
  export const inject = [
@@ -90,12 +96,17 @@ let QaSurface = (() => {
90
96
  let _instanceExtraInitializers = [];
91
97
  let _accountsRegister_decorators;
92
98
  let _accountsLogin_decorators;
99
+ let _accountsChangePassword_decorators;
100
+ let _accountsRequestPasswordReset_decorators;
93
101
  let _accountsWhoami_decorators;
94
102
  let _accountsClaimSessions_decorators;
95
103
  let _accountsOwnedSessions_decorators;
96
104
  let _accountsListOwnership_decorators;
97
105
  let _accountsUpdateProfile_decorators;
98
106
  let _accountsUpdateStarters_decorators;
107
+ let _accountsListServiceTokens_decorators;
108
+ let _accountsCreateServiceToken_decorators;
109
+ let _accountsRevokeServiceToken_decorators;
99
110
  let _describe_decorators;
100
111
  let _createSession_decorators;
101
112
  let _accessCurrent_decorators;
@@ -108,10 +119,18 @@ let QaSurface = (() => {
108
119
  let _accessUpdateAssignment_decorators;
109
120
  let _accessUpdateSkillOverride_decorators;
110
121
  let _accessSkillActivations_decorators;
122
+ let _adminSkills_decorators;
123
+ let _adminSkill_decorators;
124
+ let _adminSkillSave_decorators;
125
+ let _adminSkillDelete_decorators;
126
+ let _adminSkillValidate_decorators;
127
+ let _adminSkillTools_decorators;
111
128
  let _adminOverview_decorators;
112
129
  let _adminUsers_decorators;
113
130
  let _adminUser_decorators;
114
131
  let _adminUpdateUser_decorators;
132
+ let _adminPasswordResetRequests_decorators;
133
+ let _adminResetPassword_decorators;
115
134
  let _adminConversations_decorators;
116
135
  let _adminConversation_decorators;
117
136
  let _adminDeleteConversation_decorators;
@@ -130,6 +149,9 @@ let QaSurface = (() => {
130
149
  let _answerQuestion_decorators;
131
150
  let _cancelQuestion_decorators;
132
151
  let _readSourceFile_decorators;
152
+ let _listWorkspaceFiles_decorators;
153
+ let _readWorkspaceFile_decorators;
154
+ let _previewWorkspaceDocument_decorators;
133
155
  let _skillsList_decorators;
134
156
  let _skillsGet_decorators;
135
157
  let _skillsCreate_decorators;
@@ -144,12 +166,17 @@ let QaSurface = (() => {
144
166
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
145
167
  _accountsRegister_decorators = [Remote("accountsRegister")];
146
168
  _accountsLogin_decorators = [Remote("accountsLogin")];
169
+ _accountsChangePassword_decorators = [Remote("accountsChangePassword")];
170
+ _accountsRequestPasswordReset_decorators = [Remote("accountsRequestPasswordReset")];
147
171
  _accountsWhoami_decorators = [Remote("accountsWhoami")];
148
172
  _accountsClaimSessions_decorators = [Remote("accountsClaimSessions")];
149
173
  _accountsOwnedSessions_decorators = [Remote("accountsOwnedSessions")];
150
174
  _accountsListOwnership_decorators = [Remote("accountsListOwnership")];
151
175
  _accountsUpdateProfile_decorators = [Remote("accountsUpdateProfile")];
152
176
  _accountsUpdateStarters_decorators = [Remote("accountsUpdateStarters")];
177
+ _accountsListServiceTokens_decorators = [Remote("accountsListServiceTokens")];
178
+ _accountsCreateServiceToken_decorators = [Remote("accountsCreateServiceToken")];
179
+ _accountsRevokeServiceToken_decorators = [Remote("accountsRevokeServiceToken")];
153
180
  _describe_decorators = [Remote("describe")];
154
181
  _createSession_decorators = [Remote("createSession")];
155
182
  _accessCurrent_decorators = [Remote("accessCurrent")];
@@ -162,10 +189,18 @@ let QaSurface = (() => {
162
189
  _accessUpdateAssignment_decorators = [Remote("accessUpdateAssignment")];
163
190
  _accessUpdateSkillOverride_decorators = [Remote("accessUpdateSkillOverride")];
164
191
  _accessSkillActivations_decorators = [Remote("accessSkillActivations")];
192
+ _adminSkills_decorators = [Remote("adminSkills")];
193
+ _adminSkill_decorators = [Remote("adminSkill")];
194
+ _adminSkillSave_decorators = [Remote("adminSkillSave")];
195
+ _adminSkillDelete_decorators = [Remote("adminSkillDelete")];
196
+ _adminSkillValidate_decorators = [Remote("adminSkillValidate")];
197
+ _adminSkillTools_decorators = [Remote("adminSkillTools")];
165
198
  _adminOverview_decorators = [Remote("adminOverview")];
166
199
  _adminUsers_decorators = [Remote("adminUsers")];
167
200
  _adminUser_decorators = [Remote("adminUser")];
168
201
  _adminUpdateUser_decorators = [Remote("adminUpdateUser")];
202
+ _adminPasswordResetRequests_decorators = [Remote("adminPasswordResetRequests")];
203
+ _adminResetPassword_decorators = [Remote("adminResetPassword")];
169
204
  _adminConversations_decorators = [Remote("adminConversations")];
170
205
  _adminConversation_decorators = [Remote("adminConversation")];
171
206
  _adminDeleteConversation_decorators = [Remote("adminDeleteConversation")];
@@ -184,6 +219,9 @@ let QaSurface = (() => {
184
219
  _answerQuestion_decorators = [Remote("answerQuestion")];
185
220
  _cancelQuestion_decorators = [Remote("cancelQuestion")];
186
221
  _readSourceFile_decorators = [Remote("readSourceFile")];
222
+ _listWorkspaceFiles_decorators = [Remote("listWorkspaceFiles")];
223
+ _readWorkspaceFile_decorators = [Remote("readWorkspaceFile")];
224
+ _previewWorkspaceDocument_decorators = [Remote("previewWorkspaceDocument")];
187
225
  _skillsList_decorators = [Remote("skillsList")];
188
226
  _skillsGet_decorators = [Remote("skillsGet")];
189
227
  _skillsCreate_decorators = [Remote("skillsCreate")];
@@ -195,12 +233,17 @@ let QaSurface = (() => {
195
233
  _slashExecute_decorators = [Remote("slashExecute")];
196
234
  __esDecorate(this, null, _accountsRegister_decorators, { kind: "method", name: "accountsRegister", static: false, private: false, access: { has: obj => "accountsRegister" in obj, get: obj => obj.accountsRegister }, metadata: _metadata }, null, _instanceExtraInitializers);
197
235
  __esDecorate(this, null, _accountsLogin_decorators, { kind: "method", name: "accountsLogin", static: false, private: false, access: { has: obj => "accountsLogin" in obj, get: obj => obj.accountsLogin }, metadata: _metadata }, null, _instanceExtraInitializers);
236
+ __esDecorate(this, null, _accountsChangePassword_decorators, { kind: "method", name: "accountsChangePassword", static: false, private: false, access: { has: obj => "accountsChangePassword" in obj, get: obj => obj.accountsChangePassword }, metadata: _metadata }, null, _instanceExtraInitializers);
237
+ __esDecorate(this, null, _accountsRequestPasswordReset_decorators, { kind: "method", name: "accountsRequestPasswordReset", static: false, private: false, access: { has: obj => "accountsRequestPasswordReset" in obj, get: obj => obj.accountsRequestPasswordReset }, metadata: _metadata }, null, _instanceExtraInitializers);
198
238
  __esDecorate(this, null, _accountsWhoami_decorators, { kind: "method", name: "accountsWhoami", static: false, private: false, access: { has: obj => "accountsWhoami" in obj, get: obj => obj.accountsWhoami }, metadata: _metadata }, null, _instanceExtraInitializers);
199
239
  __esDecorate(this, null, _accountsClaimSessions_decorators, { kind: "method", name: "accountsClaimSessions", static: false, private: false, access: { has: obj => "accountsClaimSessions" in obj, get: obj => obj.accountsClaimSessions }, metadata: _metadata }, null, _instanceExtraInitializers);
200
240
  __esDecorate(this, null, _accountsOwnedSessions_decorators, { kind: "method", name: "accountsOwnedSessions", static: false, private: false, access: { has: obj => "accountsOwnedSessions" in obj, get: obj => obj.accountsOwnedSessions }, metadata: _metadata }, null, _instanceExtraInitializers);
201
241
  __esDecorate(this, null, _accountsListOwnership_decorators, { kind: "method", name: "accountsListOwnership", static: false, private: false, access: { has: obj => "accountsListOwnership" in obj, get: obj => obj.accountsListOwnership }, metadata: _metadata }, null, _instanceExtraInitializers);
202
242
  __esDecorate(this, null, _accountsUpdateProfile_decorators, { kind: "method", name: "accountsUpdateProfile", static: false, private: false, access: { has: obj => "accountsUpdateProfile" in obj, get: obj => obj.accountsUpdateProfile }, metadata: _metadata }, null, _instanceExtraInitializers);
203
243
  __esDecorate(this, null, _accountsUpdateStarters_decorators, { kind: "method", name: "accountsUpdateStarters", static: false, private: false, access: { has: obj => "accountsUpdateStarters" in obj, get: obj => obj.accountsUpdateStarters }, metadata: _metadata }, null, _instanceExtraInitializers);
244
+ __esDecorate(this, null, _accountsListServiceTokens_decorators, { kind: "method", name: "accountsListServiceTokens", static: false, private: false, access: { has: obj => "accountsListServiceTokens" in obj, get: obj => obj.accountsListServiceTokens }, metadata: _metadata }, null, _instanceExtraInitializers);
245
+ __esDecorate(this, null, _accountsCreateServiceToken_decorators, { kind: "method", name: "accountsCreateServiceToken", static: false, private: false, access: { has: obj => "accountsCreateServiceToken" in obj, get: obj => obj.accountsCreateServiceToken }, metadata: _metadata }, null, _instanceExtraInitializers);
246
+ __esDecorate(this, null, _accountsRevokeServiceToken_decorators, { kind: "method", name: "accountsRevokeServiceToken", static: false, private: false, access: { has: obj => "accountsRevokeServiceToken" in obj, get: obj => obj.accountsRevokeServiceToken }, metadata: _metadata }, null, _instanceExtraInitializers);
204
247
  __esDecorate(this, null, _describe_decorators, { kind: "method", name: "describe", static: false, private: false, access: { has: obj => "describe" in obj, get: obj => obj.describe }, metadata: _metadata }, null, _instanceExtraInitializers);
205
248
  __esDecorate(this, null, _createSession_decorators, { kind: "method", name: "createSession", static: false, private: false, access: { has: obj => "createSession" in obj, get: obj => obj.createSession }, metadata: _metadata }, null, _instanceExtraInitializers);
206
249
  __esDecorate(this, null, _accessCurrent_decorators, { kind: "method", name: "accessCurrent", static: false, private: false, access: { has: obj => "accessCurrent" in obj, get: obj => obj.accessCurrent }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -213,10 +256,18 @@ let QaSurface = (() => {
213
256
  __esDecorate(this, null, _accessUpdateAssignment_decorators, { kind: "method", name: "accessUpdateAssignment", static: false, private: false, access: { has: obj => "accessUpdateAssignment" in obj, get: obj => obj.accessUpdateAssignment }, metadata: _metadata }, null, _instanceExtraInitializers);
214
257
  __esDecorate(this, null, _accessUpdateSkillOverride_decorators, { kind: "method", name: "accessUpdateSkillOverride", static: false, private: false, access: { has: obj => "accessUpdateSkillOverride" in obj, get: obj => obj.accessUpdateSkillOverride }, metadata: _metadata }, null, _instanceExtraInitializers);
215
258
  __esDecorate(this, null, _accessSkillActivations_decorators, { kind: "method", name: "accessSkillActivations", static: false, private: false, access: { has: obj => "accessSkillActivations" in obj, get: obj => obj.accessSkillActivations }, metadata: _metadata }, null, _instanceExtraInitializers);
259
+ __esDecorate(this, null, _adminSkills_decorators, { kind: "method", name: "adminSkills", static: false, private: false, access: { has: obj => "adminSkills" in obj, get: obj => obj.adminSkills }, metadata: _metadata }, null, _instanceExtraInitializers);
260
+ __esDecorate(this, null, _adminSkill_decorators, { kind: "method", name: "adminSkill", static: false, private: false, access: { has: obj => "adminSkill" in obj, get: obj => obj.adminSkill }, metadata: _metadata }, null, _instanceExtraInitializers);
261
+ __esDecorate(this, null, _adminSkillSave_decorators, { kind: "method", name: "adminSkillSave", static: false, private: false, access: { has: obj => "adminSkillSave" in obj, get: obj => obj.adminSkillSave }, metadata: _metadata }, null, _instanceExtraInitializers);
262
+ __esDecorate(this, null, _adminSkillDelete_decorators, { kind: "method", name: "adminSkillDelete", static: false, private: false, access: { has: obj => "adminSkillDelete" in obj, get: obj => obj.adminSkillDelete }, metadata: _metadata }, null, _instanceExtraInitializers);
263
+ __esDecorate(this, null, _adminSkillValidate_decorators, { kind: "method", name: "adminSkillValidate", static: false, private: false, access: { has: obj => "adminSkillValidate" in obj, get: obj => obj.adminSkillValidate }, metadata: _metadata }, null, _instanceExtraInitializers);
264
+ __esDecorate(this, null, _adminSkillTools_decorators, { kind: "method", name: "adminSkillTools", static: false, private: false, access: { has: obj => "adminSkillTools" in obj, get: obj => obj.adminSkillTools }, metadata: _metadata }, null, _instanceExtraInitializers);
216
265
  __esDecorate(this, null, _adminOverview_decorators, { kind: "method", name: "adminOverview", static: false, private: false, access: { has: obj => "adminOverview" in obj, get: obj => obj.adminOverview }, metadata: _metadata }, null, _instanceExtraInitializers);
217
266
  __esDecorate(this, null, _adminUsers_decorators, { kind: "method", name: "adminUsers", static: false, private: false, access: { has: obj => "adminUsers" in obj, get: obj => obj.adminUsers }, metadata: _metadata }, null, _instanceExtraInitializers);
218
267
  __esDecorate(this, null, _adminUser_decorators, { kind: "method", name: "adminUser", static: false, private: false, access: { has: obj => "adminUser" in obj, get: obj => obj.adminUser }, metadata: _metadata }, null, _instanceExtraInitializers);
219
268
  __esDecorate(this, null, _adminUpdateUser_decorators, { kind: "method", name: "adminUpdateUser", static: false, private: false, access: { has: obj => "adminUpdateUser" in obj, get: obj => obj.adminUpdateUser }, metadata: _metadata }, null, _instanceExtraInitializers);
269
+ __esDecorate(this, null, _adminPasswordResetRequests_decorators, { kind: "method", name: "adminPasswordResetRequests", static: false, private: false, access: { has: obj => "adminPasswordResetRequests" in obj, get: obj => obj.adminPasswordResetRequests }, metadata: _metadata }, null, _instanceExtraInitializers);
270
+ __esDecorate(this, null, _adminResetPassword_decorators, { kind: "method", name: "adminResetPassword", static: false, private: false, access: { has: obj => "adminResetPassword" in obj, get: obj => obj.adminResetPassword }, metadata: _metadata }, null, _instanceExtraInitializers);
220
271
  __esDecorate(this, null, _adminConversations_decorators, { kind: "method", name: "adminConversations", static: false, private: false, access: { has: obj => "adminConversations" in obj, get: obj => obj.adminConversations }, metadata: _metadata }, null, _instanceExtraInitializers);
221
272
  __esDecorate(this, null, _adminConversation_decorators, { kind: "method", name: "adminConversation", static: false, private: false, access: { has: obj => "adminConversation" in obj, get: obj => obj.adminConversation }, metadata: _metadata }, null, _instanceExtraInitializers);
222
273
  __esDecorate(this, null, _adminDeleteConversation_decorators, { kind: "method", name: "adminDeleteConversation", static: false, private: false, access: { has: obj => "adminDeleteConversation" in obj, get: obj => obj.adminDeleteConversation }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -235,6 +286,9 @@ let QaSurface = (() => {
235
286
  __esDecorate(this, null, _answerQuestion_decorators, { kind: "method", name: "answerQuestion", static: false, private: false, access: { has: obj => "answerQuestion" in obj, get: obj => obj.answerQuestion }, metadata: _metadata }, null, _instanceExtraInitializers);
236
287
  __esDecorate(this, null, _cancelQuestion_decorators, { kind: "method", name: "cancelQuestion", static: false, private: false, access: { has: obj => "cancelQuestion" in obj, get: obj => obj.cancelQuestion }, metadata: _metadata }, null, _instanceExtraInitializers);
237
288
  __esDecorate(this, null, _readSourceFile_decorators, { kind: "method", name: "readSourceFile", static: false, private: false, access: { has: obj => "readSourceFile" in obj, get: obj => obj.readSourceFile }, metadata: _metadata }, null, _instanceExtraInitializers);
289
+ __esDecorate(this, null, _listWorkspaceFiles_decorators, { kind: "method", name: "listWorkspaceFiles", static: false, private: false, access: { has: obj => "listWorkspaceFiles" in obj, get: obj => obj.listWorkspaceFiles }, metadata: _metadata }, null, _instanceExtraInitializers);
290
+ __esDecorate(this, null, _readWorkspaceFile_decorators, { kind: "method", name: "readWorkspaceFile", static: false, private: false, access: { has: obj => "readWorkspaceFile" in obj, get: obj => obj.readWorkspaceFile }, metadata: _metadata }, null, _instanceExtraInitializers);
291
+ __esDecorate(this, null, _previewWorkspaceDocument_decorators, { kind: "method", name: "previewWorkspaceDocument", static: false, private: false, access: { has: obj => "previewWorkspaceDocument" in obj, get: obj => obj.previewWorkspaceDocument }, metadata: _metadata }, null, _instanceExtraInitializers);
238
292
  __esDecorate(this, null, _skillsList_decorators, { kind: "method", name: "skillsList", static: false, private: false, access: { has: obj => "skillsList" in obj, get: obj => obj.skillsList }, metadata: _metadata }, null, _instanceExtraInitializers);
239
293
  __esDecorate(this, null, _skillsGet_decorators, { kind: "method", name: "skillsGet", static: false, private: false, access: { has: obj => "skillsGet" in obj, get: obj => obj.skillsGet }, metadata: _metadata }, null, _instanceExtraInitializers);
240
294
  __esDecorate(this, null, _skillsCreate_decorators, { kind: "method", name: "skillsCreate", static: false, private: false, access: { has: obj => "skillsCreate" in obj, get: obj => obj.skillsCreate }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -252,6 +306,7 @@ let QaSurface = (() => {
252
306
  logger;
253
307
  admission;
254
308
  approvals;
309
+ fileDeleteGate;
255
310
  userQuestions;
256
311
  provenance;
257
312
  notes;
@@ -282,6 +337,14 @@ let QaSurface = (() => {
282
337
  webServer;
283
338
  disposeRoute;
284
339
  routeKey;
340
+ /**
341
+ * The HTTP API an external application asks questions through. Constructed
342
+ * always — it is cheap and stateless until a request arrives — and exposed
343
+ * on the network only while the deployment turns it on.
344
+ */
345
+ integration;
346
+ disposeIntegrationRoutes;
347
+ integrationRouteKey;
285
348
  /**
286
349
  * The quality store opens its file on first use, so a deployment that never
287
350
  * opens the admin console never grows one.
@@ -332,6 +395,10 @@ let QaSurface = (() => {
332
395
  // per-browser row, and only an administrator removes the chat itself.
333
396
  sessionFiles: createSessionEraser(),
334
397
  dropSources: (sessionId) => this.provenance.dropSession(sessionId),
398
+ // The console writes skills through the same service the owner's editor
399
+ // uses, so an administrator's save is path-checked, validated and
400
+ // published to DSH by exactly the one code path that already does it.
401
+ skills: () => this.personalSkills.service,
335
402
  logger: this.logger,
336
403
  });
337
404
  this.skillRemotes = createQaPersonalSkillRemotes({
@@ -355,17 +422,41 @@ let QaSurface = (() => {
355
422
  ?.ensureSessionAccess(token, sessionId, facts);
356
423
  },
357
424
  userWorkspace: (userId, registeredWorkspacePath) => existingQaUserWorkspace(registeredWorkspacePath, userId),
425
+ // Read only by the integration API, whose caller proved an account
426
+ // with a service token instead of a browser one. The ownership
427
+ // record stays the authority either way.
428
+ ownerIdOf: (sessionId) => this.accountRemotes.resolve(this.getConfig())?.ownerIdOf(sessionId),
358
429
  },
359
430
  // Legacy account-free sessions retain the dynamic catalog behavior;
360
431
  // role-managed sessions authorize its known entries through the frozen
361
432
  // capability policy instead.
362
- (agent) => this.tools?.activeToolNames(agent) ?? [], (token, sessionId, agent) => this.access.policyForSession(token, sessionId, agent), () => this.tools?.catalogToolNames() ?? []);
433
+ (agent) => this.tools?.activeToolNames(agent) ?? [], (owner, sessionId, agent) => this.access.policyForSessionOwner(owner, sessionId, agent), () => this.tools?.catalogToolNames() ?? []);
363
434
  this.provenance = new QaProvenanceHost(ctx, () => this.getConfig(),
364
435
  // Sharded per chat and bounded by the resolved retention policy: the
365
436
  // turn path must never rewrite history it did not touch.
366
437
  new FileQaProvenanceSnapshotStore(defaultQaProvenanceDirectoryPath(), () => this.getConfig().sources.retention, defaultLegacyProvenanceFilePath()), (error) => this.logger.error("sources.persistence-failed", {
367
438
  message: error instanceof Error ? error.message : String(error),
368
439
  }));
440
+ this.integration = new QaIntegrationService({
441
+ getConfig: () => this.getConfig(),
442
+ accounts: () => this.accountRemotes.resolve(this.getConfig()),
443
+ runner: createQaIntegrationRunner({
444
+ ctx,
445
+ getConfig: () => this.getConfig(),
446
+ accounts: () => this.accountRemotes.resolve(this.getConfig()),
447
+ admission: this.admission,
448
+ access: this.access,
449
+ sessionLog,
450
+ provenance: this.provenance,
451
+ // Resolved per call, like the files panel does it: the document plugin
452
+ // may install after this one, and a document attachment that arrives
453
+ // before it did is refused with the caller's own fallback signal.
454
+ documents: () => ctx.get("documents"),
455
+ logger: this.logger,
456
+ }),
457
+ logger: this.logger,
458
+ version: qaSurfaceVersion(),
459
+ });
369
460
  // The one place a composed gate's `ask` becomes a decision for an attested
370
461
  // chat: refused outright while approvals are blocked, parked for the
371
462
  // operator's answer while they are interactive. The question seam shares
@@ -373,8 +464,15 @@ let QaSurface = (() => {
373
464
  const ownership = new QaSessionOwnership((sessionId) => this.admission.knowsSession(sessionId));
374
465
  this.approvals = new QaApprovalGate(ctx, () => this.getConfig().interaction.approvals === "interactive", ownership, this.logger);
375
466
  this.userQuestions = new QaQuestionGate(ctx, () => this.getConfig().interaction.questions === "interactive", ownership, this.logger);
467
+ // The destructive tool of the QA catalog composes the same approval flow
468
+ // around itself: registered without `prepend`, it sits inside the approval
469
+ // gate and answers `ask` for every file_delete call of an attested chat,
470
+ // so the interactive card parks it for the operator (or the blocked
471
+ // configuration refuses it — nothing is deleted without a person).
472
+ this.fileDeleteGate = new QaFileDeleteGate(ctx, ownership);
376
473
  this.approvals.install();
377
474
  this.userQuestions.install();
475
+ this.fileDeleteGate.install();
378
476
  // The identity note and the provenance rule ride the conversation as
379
477
  // durable context messages, delegated experts included: the QA preset's
380
478
  // complete persona closes the system prompt to plugins, the conversation
@@ -394,6 +492,7 @@ let QaSurface = (() => {
394
492
  ctx.effect(() => async () => this.logger.close(), "dsh-qa-surface.logger");
395
493
  ctx.effect(() => () => this.admission.dispose(), "dsh-qa-surface.lockdown-policies");
396
494
  ctx.effect(() => () => this.approvals.dispose(), "dsh-qa-surface.approvals");
495
+ ctx.effect(() => () => this.fileDeleteGate.dispose(), "dsh-qa-surface.file-delete-gate");
397
496
  ctx.effect(() => () => this.userQuestions.dispose(), "dsh-qa-surface.user-questions");
398
497
  ctx.effect(() => () => this.provenance.dispose(), "dsh-qa-surface.provenance");
399
498
  ctx.effect(() => () => this.notes.dispose(), "dsh-qa-surface.prompt-notes");
@@ -411,12 +510,17 @@ let QaSurface = (() => {
411
510
  });
412
511
  ctx.effect(() => () => this.tools.dispose(), "dsh-qa-surface.qa-tools");
413
512
  // The root index gains one head script: non-loopback hostnames continue
414
- // into /qa, the loopback operator keeps the full harness UI.
415
- ctx.on("webserver/index-inject", (table) => {
416
- const row = entryRedirectRow(this.getConfig());
417
- if (row !== undefined)
418
- table.push(row);
419
- });
513
+ // into /qa, the loopback operator keeps the full harness UI. Not in the
514
+ // kiosk composition: there the index itself is served under /qa, so the
515
+ // script would meet its own target on every load and reload the page in
516
+ // a loop, and the server policy already routes the site root into /qa.
517
+ if (!qaKioskDeployment()) {
518
+ ctx.on("webserver/index-inject", (table) => {
519
+ const row = entryRedirectRow(this.getConfig());
520
+ if (row !== undefined)
521
+ table.push(row);
522
+ });
523
+ }
420
524
  ctx.inject(["settings"], (settingsCtx) => {
421
525
  settingsCtx.settings.installSection(ctx, QA_SURFACE_SETTINGS_NAMESPACE, ConfigSchema, entry, {
422
526
  setSource: (source) => {
@@ -425,6 +529,7 @@ let QaSurface = (() => {
425
529
  onChange: () => {
426
530
  const config = this.getConfig();
427
531
  this.refreshRoute();
532
+ this.refreshIntegrationRoutes();
428
533
  this.warnDocumentsMoved();
429
534
  this.warnLegacySlashDefaults();
430
535
  this.logger.info("config.updated", {
@@ -441,10 +546,14 @@ let QaSurface = (() => {
441
546
  ctx.inject(["webServer"], (webContext) => {
442
547
  this.webServer = webContext.webServer;
443
548
  this.refreshRoute();
549
+ this.refreshIntegrationRoutes();
444
550
  webContext.effect(() => () => {
445
551
  this.disposeRoute?.();
446
552
  this.disposeRoute = undefined;
447
553
  this.routeKey = undefined;
554
+ this.disposeIntegrationRoutes?.();
555
+ this.disposeIntegrationRoutes = undefined;
556
+ this.integrationRouteKey = undefined;
448
557
  this.webServer = undefined;
449
558
  }, "dsh-qa-surface.navigation-route");
450
559
  });
@@ -570,6 +679,18 @@ let QaSurface = (() => {
570
679
  return this.accountRemotes.login(email, password);
571
680
  }
572
681
  /** Identity probe; safe to call with an empty or expired token. */
682
+ accountsChangePassword(token, currentPassword, nextPassword) {
683
+ return this.accountRemotes.changePassword(token, currentPassword, nextPassword);
684
+ }
685
+ /**
686
+ * The sign-in screen's "забыли пароль?" path. It answers the same way for
687
+ * every address: whether an account exists is not something this endpoint
688
+ * tells a caller.
689
+ */
690
+ accountsRequestPasswordReset(email) {
691
+ this.accountRemotes.requestPasswordReset(email);
692
+ return { accepted: true };
693
+ }
573
694
  accountsWhoami(token) {
574
695
  return this.accountRemotes.whoami(token);
575
696
  }
@@ -610,6 +731,31 @@ let QaSurface = (() => {
610
731
  accountsUpdateStarters(token, input) {
611
732
  return this.accountRemotes.updateStarters(token, input);
612
733
  }
734
+ /**
735
+ * The caller's own integration tokens. The list is the account's own: the
736
+ * token names it, and a secret is never part of a summary, so reading this
737
+ * cannot repeat a credential that was already handed over.
738
+ */
739
+ accountsListServiceTokens(token) {
740
+ return this.accountRemotes.listServiceTokens(token);
741
+ }
742
+ /**
743
+ * Mint one integration token for the caller. The answer carries the
744
+ * plaintext exactly once — nothing stores it, so nothing can repeat it — and
745
+ * the account is always the authenticated one: there is no owner field on
746
+ * the wire.
747
+ */
748
+ accountsCreateServiceToken(token, input) {
749
+ return this.accountRemotes.createServiceToken(token, input);
750
+ }
751
+ /**
752
+ * Revoke one of the caller's own integration tokens. This is the immediate
753
+ * kill switch the profile page offers: the next request that presents the
754
+ * credential is refused, without waiting for its expiry.
755
+ */
756
+ accountsRevokeServiceToken(token, tokenId) {
757
+ return this.accountRemotes.revokeServiceToken(token, tokenId);
758
+ }
613
759
  /**
614
760
  * Serve the effective QA configuration to the browser. The DSH gateway pins
615
761
  * settings RPCs to loopback, so a browser served over the LAN always sees
@@ -745,6 +891,38 @@ let QaSurface = (() => {
745
891
  // Administrative console. Every method names its permission inside the
746
892
  // service; the wire carries a token and never an identity.
747
893
  // ---------------------------------------------------------------------------
894
+ /**
895
+ * The skill catalog of one scope: the deployment's shared skills, or the
896
+ * personal skills of the account the scope names. Reading another account's
897
+ * skills is an administrative act, so it needs `skills.manage`.
898
+ */
899
+ adminSkills(token, scope) {
900
+ return this.accountRemotes.runAsync(() => this.admin.skills(token, scope));
901
+ }
902
+ /** One skill file with the body, revision and administrator mark. */
903
+ adminSkill(token, scope, name) {
904
+ return this.accountRemotes.runAsync(() => this.admin.skill(token, scope, name));
905
+ }
906
+ /**
907
+ * Create (`name` null) or replace one skill in place. The write is recorded
908
+ * as this administrator's, and the owner sees that it was.
909
+ */
910
+ adminSkillSave(token, scope, name, input) {
911
+ return this.accountRemotes.runAsync(() => this.admin.saveSkill(token, scope, name, input));
912
+ }
913
+ /** Remove one skill into the trash beside its own skills root. */
914
+ adminSkillDelete(token, scope, name, expectedRevision) {
915
+ return this.accountRemotes.runAsync(() => this.admin.removeSkill(token, scope, name, expectedRevision));
916
+ }
917
+ /** The file a save would write, plus every diagnostic for the draft. */
918
+ adminSkillValidate(token, scope, name, input) {
919
+ return this.accountRemotes.runAsync(() => this.admin.validateSkill(token, scope, name, input));
920
+ }
921
+ /** The tool catalog the picker offers for one scope. */
922
+ async adminSkillTools(token, scope) {
923
+ const tools = await this.accountRemotes.runAsync(() => this.admin.skillTools(token, scope));
924
+ return { tools };
925
+ }
748
926
  /** Counters, attention lines and the newest quality signals. */
749
927
  adminOverview(token) {
750
928
  return this.accountRemotes.runAsync(() => this.admin.overview(token));
@@ -759,6 +937,12 @@ let QaSurface = (() => {
759
937
  adminUpdateUser(token, userId, update) {
760
938
  return this.accountRemotes.runAsync(() => this.admin.updateUser(token, userId, update));
761
939
  }
940
+ adminPasswordResetRequests(token) {
941
+ return this.accountRemotes.runAsync(() => this.admin.passwordResetRequests(token));
942
+ }
943
+ adminResetPassword(token, userId, password) {
944
+ return this.accountRemotes.runAsync(() => this.admin.resetUserPassword(token, userId, password));
945
+ }
762
946
  adminConversations(token, query, cursor, limit) {
763
947
  return this.accountRemotes.runAsync(() => this.admin.conversations(token, query, cursor ?? undefined, limit ?? undefined));
764
948
  }
@@ -912,6 +1096,105 @@ let QaSurface = (() => {
912
1096
  throw sourcePreviewRefusal(error instanceof QaSourcePreviewError ? error.reason : "unavailable");
913
1097
  }
914
1098
  }
1099
+ /**
1100
+ * List one directory of the chat's own workspace for the files panel.
1101
+ *
1102
+ * Browsing is deliberately narrower than the source preview: it stays inside
1103
+ * the chat's own directory tree, so a visitor can look at what their
1104
+ * conversation produced and at nothing else of the deployment.
1105
+ * `readWorkspaceFile` opens one entry of such a listing.
1106
+ */
1107
+ async listWorkspaceFiles(token, sessionId, path) {
1108
+ await this.admission.secureSession(token, sessionId);
1109
+ const { config, cwd } = await this.browseContext(sessionId);
1110
+ try {
1111
+ return await listWorkspaceDirectory({
1112
+ dirPath: path ?? "",
1113
+ cwd,
1114
+ maxEntries: config.sources.filePreview.maxListingEntries,
1115
+ });
1116
+ }
1117
+ catch (error) {
1118
+ throw this.browseRefusal("workspace.list-refused", sessionId, error);
1119
+ }
1120
+ }
1121
+ /**
1122
+ * Read one file of the chat's workspace: text when it decodes as UTF-8, bytes
1123
+ * when it does not, so a document the agent produced can be downloaded while
1124
+ * a note previews inline. The readable roots are the ones the model itself
1125
+ * may read; unlike the source preview this does not require the file to be
1126
+ * recorded evidence, because the panel is browsing a tree rather than
1127
+ * reopening a cited source.
1128
+ */
1129
+ async readWorkspaceFile(token, sessionId, path) {
1130
+ await this.admission.secureSession(token, sessionId);
1131
+ const { config, cwd } = await this.browseContext(sessionId);
1132
+ const attachmentRoot = this.admission.attachmentRoot();
1133
+ try {
1134
+ return await readWorkspaceFile({
1135
+ filePath: path ?? "",
1136
+ cwd,
1137
+ sharedReadOnlyRoots: config.lockdown.sharedReadOnlyRoots,
1138
+ ...(attachmentRoot === undefined ? {} : { attachmentRoot }),
1139
+ maxBytes: config.sources.filePreview.maxBytes,
1140
+ maxMarkdownRenderBytes: config.sources.filePreview.maxMarkdownRenderBytes,
1141
+ });
1142
+ }
1143
+ catch (error) {
1144
+ throw this.browseRefusal("workspace.read-refused", sessionId, error);
1145
+ }
1146
+ }
1147
+ /**
1148
+ * Render one Word document of the chat's workspace as a PDF for the panel.
1149
+ *
1150
+ * The conversion is the document pipeline's own, reached through the service
1151
+ * `@yadsh/dsh-documents` publishes for its Host siblings — the panel gets the
1152
+ * same providers, limits and refusals the `document_*` tools see, and a
1153
+ * deployment without that plugin refuses instead of showing a guess. The
1154
+ * produced PDF is read back here and travels as base64, so the audience never
1155
+ * reaches an unfenced byte route.
1156
+ */
1157
+ async previewWorkspaceDocument(token, sessionId, path) {
1158
+ await this.admission.secureSession(token, sessionId);
1159
+ const { config, cwd } = await this.browseContext(sessionId);
1160
+ const attachmentRoot = this.admission.attachmentRoot();
1161
+ try {
1162
+ return await previewConvertibleDocument({
1163
+ filePath: path ?? "",
1164
+ cwd,
1165
+ sharedReadOnlyRoots: config.lockdown.sharedReadOnlyRoots,
1166
+ ...(attachmentRoot === undefined ? {} : { attachmentRoot }),
1167
+ documents: () => this.ctx.get("documents"),
1168
+ sessionId,
1169
+ maxBytes: config.sources.filePreview.maxBytes,
1170
+ });
1171
+ }
1172
+ catch (error) {
1173
+ throw this.browseRefusal("workspace.preview-refused", sessionId, error);
1174
+ }
1175
+ }
1176
+ /**
1177
+ * Shared admission facts of both browse methods: the preview switch is what
1178
+ * opens the panel's file reading at all, and the chat's cwd is the tree that
1179
+ * browsing is confined to.
1180
+ */
1181
+ async browseContext(sessionId) {
1182
+ const config = this.getConfig();
1183
+ if (!config.sources.filePreview.enabled) {
1184
+ throw sourcePreviewRefusal("unavailable");
1185
+ }
1186
+ const agent = this.ctx.agents.get((await import("@deepseek-ai/dsh-session/types")).SessionId(sessionId));
1187
+ const cwd = agent?.session.header.cwd;
1188
+ if (cwd === undefined)
1189
+ throw sourcePreviewRefusal("unavailable");
1190
+ return { config, cwd };
1191
+ }
1192
+ /** One browse refusal, logged with its coarse reason and rethrown for the wire. */
1193
+ browseRefusal(event, sessionId, error) {
1194
+ const reason = error instanceof QaSourcePreviewError ? error.reason : "unavailable";
1195
+ this.logger.debug(event, { sessionId, reason });
1196
+ return sourcePreviewRefusal(reason);
1197
+ }
915
1198
  /** Every personal skill of the token's account, sorted by name. */
916
1199
  skillsList(token) {
917
1200
  return this.skillRemotes.list(token);
@@ -975,11 +1258,57 @@ let QaSurface = (() => {
975
1258
  slashExecute(token, sessionId, line, attachments) {
976
1259
  return this.slashRemotes.execute(token, sessionId, line, attachments);
977
1260
  }
978
- refreshRoute() {
1261
+ /**
1262
+ * Register (or drop) the integration API's routes for the current config.
1263
+ *
1264
+ * The key is the whole registration decision — whether the API is on and
1265
+ * where it lives — so a config change that does not move the endpoints
1266
+ * leaves the live routes alone, and turning the API off disposes them
1267
+ * immediately rather than at the next restart.
1268
+ */
1269
+ refreshIntegrationRoutes() {
979
1270
  const config = this.getConfig();
980
- const key = config.enabled
981
- ? `${config.route.path}:${config.route.matchChildren}`
1271
+ const key = config.integration.enabled && config.accounts.enabled
1272
+ ? `${config.integration.basePath}:${String(config.integration.maxRequestBytes)}:${String(config.integration.maxAttachmentBytes)}`
982
1273
  : undefined;
1274
+ if (key === this.integrationRouteKey)
1275
+ return;
1276
+ this.disposeIntegrationRoutes?.();
1277
+ this.disposeIntegrationRoutes = undefined;
1278
+ this.integrationRouteKey = undefined;
1279
+ if (key === undefined || this.webServer === undefined)
1280
+ return;
1281
+ try {
1282
+ this.disposeIntegrationRoutes = registerQaIntegrationRoutes(this.webServer, { config, service: this.integration, logger: this.logger });
1283
+ this.integrationRouteKey = key;
1284
+ this.logger.info("integration.routes-registered", {
1285
+ basePath: config.integration.basePath,
1286
+ });
1287
+ }
1288
+ catch (error) {
1289
+ // A collision on (kind, path) means another plugin owns the namespace.
1290
+ // The page and the accounts keep working; only the API stays off, and
1291
+ // the operator sees why.
1292
+ this.logger.error("integration.routes-failed", {
1293
+ basePath: config.integration.basePath,
1294
+ message: error instanceof Error ? error.message : String(error),
1295
+ });
1296
+ }
1297
+ }
1298
+ refreshRoute() {
1299
+ const config = this.getConfig();
1300
+ // Kiosk: the harness route policy owns the QA paths and serves the
1301
+ // application there directly. This plugin's navigation redirect route
1302
+ // must not register — a named route shadows the fallback seat, so it
1303
+ // would bounce every /qa navigation to /?marker while the policy bounces
1304
+ // / back to /qa: a redirect loop. The kiosk key keeps the transition
1305
+ // machinery honest (dispose on entering kiosk, no re-registration, one
1306
+ // log line per transition).
1307
+ const key = qaKioskDeployment()
1308
+ ? "kiosk"
1309
+ : config.enabled
1310
+ ? `${config.route.path}:${config.route.matchChildren}`
1311
+ : undefined;
983
1312
  if (key === this.routeKey)
984
1313
  return;
985
1314
  this.disposeRoute?.();
@@ -987,6 +1316,13 @@ let QaSurface = (() => {
987
1316
  this.routeKey = undefined;
988
1317
  if (key === undefined || this.webServer === undefined)
989
1318
  return;
1319
+ if (key === "kiosk") {
1320
+ this.logger.info("route.kiosk", {
1321
+ message: "DSH_UI_MODE=qa: the navigation redirect route is not registered; the harness route policy serves the QA surface directly",
1322
+ });
1323
+ this.routeKey = key;
1324
+ return;
1325
+ }
990
1326
  this.disposeRoute = registerQaNavigationRoute(this.webServer, config, {
991
1327
  launchToken: this.launchToken,
992
1328
  });