@yadsh/dsh-qa-surface 0.17.0 → 0.19.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 (213) hide show
  1. package/README.md +35 -25
  2. package/compatibility.json +1 -1
  3. package/lib/account-remotes.js +4 -0
  4. package/lib/account-remotes.js.map +1 -1
  5. package/lib/accounts/database.js +86 -1
  6. package/lib/accounts/database.js.map +1 -1
  7. package/lib/accounts/file.js.map +1 -1
  8. package/lib/accounts/store.js +111 -5
  9. package/lib/accounts/store.js.map +1 -1
  10. package/lib/accounts/token.js +8 -2
  11. package/lib/accounts/token.js.map +1 -1
  12. package/lib/admin/metrics.js +15 -2
  13. package/lib/admin/metrics.js.map +1 -1
  14. package/lib/admin/read-budget.js +28 -0
  15. package/lib/admin/read-budget.js.map +1 -0
  16. package/lib/admin/service.js +135 -40
  17. package/lib/admin/service.js.map +1 -1
  18. package/lib/admin/session-log.js +63 -4
  19. package/lib/admin/session-log.js.map +1 -1
  20. package/lib/attachment-access.js +147 -0
  21. package/lib/attachment-access.js.map +1 -0
  22. package/lib/chat-sharing/native-fence.js +20 -0
  23. package/lib/chat-sharing/native-fence.js.map +1 -1
  24. package/lib/chat-sharing/search.js +89 -0
  25. package/lib/chat-sharing/search.js.map +1 -0
  26. package/lib/chat-sharing/transport.js +10 -9
  27. package/lib/chat-sharing/transport.js.map +1 -1
  28. package/lib/chat-sharing/uploads.js +79 -6
  29. package/lib/chat-sharing/uploads.js.map +1 -1
  30. package/lib/cli.js +13 -0
  31. package/lib/cli.js.map +1 -1
  32. package/lib/client/QaAccountsController.js +61 -0
  33. package/lib/client/QaAccountsController.js.map +1 -1
  34. package/lib/client/QaRouteController.js +3 -1
  35. package/lib/client/QaRouteController.js.map +1 -1
  36. package/lib/client/QaSessionController.js +141 -35
  37. package/lib/client/QaSessionController.js.map +1 -1
  38. package/lib/client/QaSurface.js +43 -13
  39. package/lib/client/QaSurface.js.map +1 -1
  40. package/lib/client/account-sessions.js +74 -8
  41. package/lib/client/account-sessions.js.map +1 -1
  42. package/lib/client/admin/CapabilityPicker.js +50 -0
  43. package/lib/client/admin/CapabilityPicker.js.map +1 -0
  44. package/lib/client/admin/CoverageNotice.js +9 -0
  45. package/lib/client/admin/CoverageNotice.js.map +1 -0
  46. package/lib/client/admin/QaAdmin.js +3 -150
  47. package/lib/client/admin/QaAdmin.js.map +1 -1
  48. package/lib/client/admin/RoleEditor.js +46 -0
  49. package/lib/client/admin/RoleEditor.js.map +1 -0
  50. package/lib/client/admin/capability-selection.js +63 -0
  51. package/lib/client/admin/capability-selection.js.map +1 -0
  52. package/lib/client/admin/pages/Conversations.js +35 -17
  53. package/lib/client/admin/pages/Conversations.js.map +1 -1
  54. package/lib/client/admin/pages/Overview.js +3 -2
  55. package/lib/client/admin/pages/Overview.js.map +1 -1
  56. package/lib/client/admin/pages/Quality.js +77 -20
  57. package/lib/client/admin/pages/Quality.js.map +1 -1
  58. package/lib/client/admin/pages/SkillFiles.js +14 -3
  59. package/lib/client/admin/pages/SkillFiles.js.map +1 -1
  60. package/lib/client/admin/shared.js +68 -3
  61. package/lib/client/admin/shared.js.map +1 -1
  62. package/lib/client/components/QaAuthGate.js +31 -28
  63. package/lib/client/components/QaAuthGate.js.map +1 -1
  64. package/lib/client/components/QaChangelog.js +71 -0
  65. package/lib/client/components/QaChangelog.js.map +1 -1
  66. package/lib/client/components/QaComposer.js +27 -4
  67. package/lib/client/components/QaComposer.js.map +1 -1
  68. package/lib/client/components/QaFormItem.js +63 -0
  69. package/lib/client/components/QaFormItem.js.map +1 -0
  70. package/lib/client/components/QaInputForm.js +281 -0
  71. package/lib/client/components/QaInputForm.js.map +1 -0
  72. package/lib/client/components/QaModal.js +17 -4
  73. package/lib/client/components/QaModal.js.map +1 -1
  74. package/lib/client/components/QaRequestWait.js +15 -0
  75. package/lib/client/components/QaRequestWait.js.map +1 -0
  76. package/lib/client/forms-styles.js +14 -0
  77. package/lib/client/forms-styles.js.map +1 -0
  78. package/lib/client/forms.js +40 -0
  79. package/lib/client/forms.js.map +1 -0
  80. package/lib/client/index.js +18 -2
  81. package/lib/client/index.js.map +1 -1
  82. package/lib/client/oidc-browser.js +39 -0
  83. package/lib/client/oidc-browser.js.map +1 -0
  84. package/lib/client/panels/PanelHost.js +23 -1
  85. package/lib/client/panels/PanelHost.js.map +1 -1
  86. package/lib/client/project-session-state.js.map +1 -1
  87. package/lib/client/request-wait-styles.js +7 -0
  88. package/lib/client/request-wait-styles.js.map +1 -0
  89. package/lib/client/settings/sections/interface.js +2 -1
  90. package/lib/client/settings/sections/interface.js.map +1 -1
  91. package/lib/client/settings/sections/status.js +3 -1
  92. package/lib/client/settings/sections/status.js.map +1 -1
  93. package/lib/client/styles.js +5 -3
  94. package/lib/client/styles.js.map +1 -1
  95. package/lib/client/types.js.map +1 -1
  96. package/lib/client.js +471 -72918
  97. package/lib/client.js.map +1 -1
  98. package/lib/config-resolvers/accounts.js +5 -4
  99. package/lib/config-resolvers/accounts.js.map +1 -1
  100. package/lib/config-resolvers/defaults.js +16 -0
  101. package/lib/config-resolvers/defaults.js.map +1 -1
  102. package/lib/config-resolvers/oidc.js +53 -0
  103. package/lib/config-resolvers/oidc.js.map +1 -0
  104. package/lib/config.js +71 -0
  105. package/lib/config.js.map +1 -1
  106. package/lib/filesystem-intent.js +39 -0
  107. package/lib/filesystem-intent.js.map +1 -0
  108. package/lib/forms/attachments.js +230 -0
  109. package/lib/forms/attachments.js.map +1 -0
  110. package/lib/forms/errors.js +37 -0
  111. package/lib/forms/errors.js.map +1 -0
  112. package/lib/forms/fingerprint.js +21 -0
  113. package/lib/forms/fingerprint.js.map +1 -0
  114. package/lib/forms/gate.js +125 -0
  115. package/lib/forms/gate.js.map +1 -0
  116. package/lib/forms/schema.js +182 -0
  117. package/lib/forms/schema.js.map +1 -0
  118. package/lib/forms/service.js +220 -0
  119. package/lib/forms/service.js.map +1 -0
  120. package/lib/forms/store.js +194 -0
  121. package/lib/forms/store.js.map +1 -0
  122. package/lib/forms/types.js +2 -0
  123. package/lib/forms/types.js.map +1 -0
  124. package/lib/forms/validation.js +113 -0
  125. package/lib/forms/validation.js.map +1 -0
  126. package/lib/index.js +271 -81
  127. package/lib/index.js.map +1 -1
  128. package/lib/oidc/http.js +30 -0
  129. package/lib/oidc/http.js.map +1 -0
  130. package/lib/oidc/service.js +250 -0
  131. package/lib/oidc/service.js.map +1 -0
  132. package/lib/qa-tools/activation-manager.js +24 -2
  133. package/lib/qa-tools/activation-manager.js.map +1 -1
  134. package/lib/qa-tools/catalog.js +11 -1
  135. package/lib/qa-tools/catalog.js.map +1 -1
  136. package/lib/qa-tools/index.js +27 -0
  137. package/lib/qa-tools/index.js.map +1 -1
  138. package/lib/qa-tools/request-user-form.js +121 -0
  139. package/lib/qa-tools/request-user-form.js.map +1 -0
  140. package/lib/request-scheduler.js +235 -0
  141. package/lib/request-scheduler.js.map +1 -0
  142. package/lib/resolve-config.js +2 -0
  143. package/lib/resolve-config.js.map +1 -1
  144. package/lib/secure-session.js +60 -60
  145. package/lib/secure-session.js.map +1 -1
  146. package/lib/session-create.js +125 -0
  147. package/lib/session-create.js.map +1 -0
  148. package/lib/typert.host.js +4636 -1103
  149. package/lib/typert.remote-client.d.ts +135 -5
  150. package/lib/typert.remote-client.js +4502 -1095
  151. package/lib/types/accounts/database.d.ts +8 -0
  152. package/lib/types/accounts/file.d.ts +2 -0
  153. package/lib/types/accounts/store.d.ts +17 -0
  154. package/lib/types/accounts/token.d.ts +3 -1
  155. package/lib/types/admin/metrics.d.ts +5 -1
  156. package/lib/types/admin/read-budget.d.ts +8 -0
  157. package/lib/types/admin/service.d.ts +17 -0
  158. package/lib/types/admin/session-log.d.ts +30 -1
  159. package/lib/types/attachment-access.d.ts +29 -0
  160. package/lib/types/chat-sharing/search.d.ts +5 -0
  161. package/lib/types/chat-sharing/uploads.d.ts +2 -1
  162. package/lib/types/client/QaAccountsController.d.ts +4 -0
  163. package/lib/types/client/QaRouteController.d.ts +1 -0
  164. package/lib/types/client/QaSessionController.d.ts +24 -12
  165. package/lib/types/client/QaSurface.d.ts +2 -0
  166. package/lib/types/client/admin/CapabilityPicker.d.ts +14 -0
  167. package/lib/types/client/admin/CoverageNotice.d.ts +6 -0
  168. package/lib/types/client/admin/RoleEditor.d.ts +15 -0
  169. package/lib/types/client/admin/capability-selection.d.ts +27 -0
  170. package/lib/types/client/admin/shared.d.ts +39 -1
  171. package/lib/types/client/components/QaAuthGate.d.ts +2 -0
  172. package/lib/types/client/components/QaFormItem.d.ts +20 -0
  173. package/lib/types/client/components/QaInputForm.d.ts +18 -0
  174. package/lib/types/client/components/QaRequestWait.d.ts +6 -0
  175. package/lib/types/client/forms-styles.d.ts +2 -0
  176. package/lib/types/client/forms.d.ts +23 -0
  177. package/lib/types/client/oidc-browser.d.ts +12 -0
  178. package/lib/types/client/project-session-state.d.ts +2 -4
  179. package/lib/types/client/request-wait-styles.d.ts +2 -0
  180. package/lib/types/client/types.d.ts +10 -4
  181. package/lib/types/config-resolvers/oidc.d.ts +3 -0
  182. package/lib/types/filesystem-intent.d.ts +12 -0
  183. package/lib/types/forms/attachments.d.ts +51 -0
  184. package/lib/types/forms/errors.d.ts +11 -0
  185. package/lib/types/forms/fingerprint.d.ts +9 -0
  186. package/lib/types/forms/gate.d.ts +33 -0
  187. package/lib/types/forms/schema.d.ts +72 -0
  188. package/lib/types/forms/service.d.ts +15 -0
  189. package/lib/types/forms/store.d.ts +25 -0
  190. package/lib/types/forms/types.d.ts +215 -0
  191. package/lib/types/forms/validation.d.ts +20 -0
  192. package/lib/types/index.d.ts +33 -12
  193. package/lib/types/oidc/http.d.ts +5 -0
  194. package/lib/types/oidc/service.d.ts +30 -0
  195. package/lib/types/oidc.js +2 -0
  196. package/lib/types/oidc.js.map +1 -0
  197. package/lib/types/qa-tools/activation-manager.d.ts +5 -0
  198. package/lib/types/qa-tools/catalog.d.ts +4 -1
  199. package/lib/types/qa-tools/index.d.ts +5 -1
  200. package/lib/types/qa-tools/request-user-form.d.ts +5 -0
  201. package/lib/types/request-scheduler.d.ts +28 -0
  202. package/lib/types/secure-session.d.ts +13 -26
  203. package/lib/types/session-create.d.ts +29 -0
  204. package/lib/types/types/accounts.d.ts +2 -0
  205. package/lib/types/types/admin.d.ts +6 -0
  206. package/lib/types/types/config.d.ts +6 -0
  207. package/lib/types/types/oidc.d.ts +36 -0
  208. package/lib/types/types/session.d.ts +8 -0
  209. package/lib/types/types.d.ts +2 -0
  210. package/lib/types/user-workspace.d.ts +4 -6
  211. package/lib/user-workspace.js +13 -67
  212. package/lib/user-workspace.js.map +1 -1
  213. package/package.json +66 -62
package/lib/index.js CHANGED
@@ -32,7 +32,6 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
32
32
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
33
  done = true;
34
34
  };
35
- import { randomUUID } from "node:crypto";
36
35
  import { readSourceImage as readSourceImagePreview } from "./provenance/source-image.js";
37
36
  import path from "node:path";
38
37
  import { defaultAccountsFilePath } from "./accounts/store.js";
@@ -44,9 +43,9 @@ import { QaIntegrationQuestionPolicy } from "./integration/question-policy.js";
44
43
  import { qaChatPublishedFile, qaChatAttachmentPath, } from "./chat-sharing/resources.js";
45
44
  import { installQaChatUploads } from "./chat-sharing/uploads.js";
46
45
  import { forkQaSession } from "./session-fork-host.js";
46
+ import { createQaSession } from "./session-create.js";
47
47
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
48
48
  import { SessionId } from "@deepseek-ai/dsh-session/types";
49
- import { WorkspaceId } from "@deepseek-ai/dsh-workspace";
50
49
  import { createHostLoggerSink, getPluginLogger, } from "@yadsh/dsh-plugin-log";
51
50
  import { ConfigSchema, qaSurfaceVersion, readConfigRefs, resolveConfig, } from "./config.js";
52
51
  import { createQaAccountRemotes } from "./account-remotes.js";
@@ -55,15 +54,22 @@ import { QaQualityStore } from "./admin/quality-store.js";
55
54
  import { createSessionEraser, createSessionLogReader, } from "./admin/session-log.js";
56
55
  import { createQaPersonalSkillRemotes, QaPersonalSkillsHost, } from "./personal-skills/index.js";
57
56
  import { QaApprovalGate } from "./approvals.js";
58
- import { QaAttestationError, qaAttestationFailureMessage, } from "./attestation.js";
57
+ import { QaAttestationError } from "./attestation.js";
59
58
  import { QaFileDeleteGate } from "./qa-tools/file-delete-gate.js";
60
59
  import { QaQuestionGate } from "./questions.js";
60
+ import { QaFormStore } from "./forms/store.js";
61
+ import { QaFormService } from "./forms/service.js";
62
+ import { QaFormGate } from "./forms/gate.js";
63
+ import { QaFormAttachmentAdapter, } from "./forms/attachments.js";
64
+ import { formFailure, QaFormError } from "./forms/errors.js";
61
65
  import { QaSessionOwnership } from "./session-ownership.js";
62
66
  import { QaLlmIdentity } from "./llm-identity.js";
63
67
  import { registerTurnFailureLog } from "./turn-failure-log.js";
64
68
  import { entryRedirectRow } from "./entry-redirect.js";
65
69
  import { qaKioskDeployment } from "./ui-mode.js";
66
70
  import { registerQaNavigationRoute } from "./host-route.js";
71
+ import { QaOidcService, oidcRefusal } from "./oidc/service.js";
72
+ import { registerQaOidcRoute } from "./oidc/http.js";
67
73
  import { makeLaunchTokenSource } from "./launch-token.js";
68
74
  import { QaIntegrationPrincipalBindings } from "./integration-principals.js";
69
75
  import { registerQaIntegrationRoutes } from "./integration/http.js";
@@ -71,11 +77,10 @@ import { createQaIntegrationRunner } from "./integration/host-runner.js";
71
77
  import { QaIntegrationService } from "./integration/service.js";
72
78
  import { QaPolicyAdmission } from "./secure-session.js";
73
79
  import { QaAccessService } from "./access/service.js";
74
- import { applySessionModelPolicy } from "./session-model.js";
75
80
  import { userInvocableSkillNames } from "./access/model.js";
76
81
  import { createQaSlashRemotes } from "./slash/remotes.js";
77
82
  import { QaPromptNotes } from "./prompt-notes.js";
78
- import { qaActiveRequests, qaQueueStatus } from "./request-queue.js";
83
+ import { QaRequestScheduler, qaRequestFamilyBusy, qaRequestRoots, } from "./request-scheduler.js";
79
84
  import { QaTools } from "./qa-tools/index.js";
80
85
  import { docsDefaultVersionOf } from "./config-resolvers/tools.js";
81
86
  import { QaProvenanceHost } from "./provenance/host-store.js";
@@ -83,7 +88,7 @@ import { defaultLegacyProvenanceFilePath, defaultQaProvenanceDirectoryPath, File
83
88
  import { previewConvertibleDocument, previewSourceDocument, } from "./provenance/document-preview.js";
84
89
  import { readWorkspaceFileChunk } from "./provenance/file-download.js";
85
90
  import { listWorkspaceDirectory, readSourceFilePreview, readWorkspaceFile, QaSourcePreviewError, } from "./provenance/file-preview.js";
86
- import { existingQaUserWorkspace, prepareQaUserWorkspace, } from "./user-workspace.js";
91
+ import { existingQaUserWorkspace } from "./user-workspace.js";
87
92
  export const name = "qa-surface";
88
93
  export const inject = [
89
94
  "agents",
@@ -139,6 +144,9 @@ let QaSurface = (() => {
139
144
  let _instanceExtraInitializers = [];
140
145
  let _accountsRegister_decorators;
141
146
  let _accountsLogin_decorators;
147
+ let _accountsOidcStart_decorators;
148
+ let _accountsOidcComplete_decorators;
149
+ let _accountsOidcLogout_decorators;
142
150
  let _accountsChangePassword_decorators;
143
151
  let _accountsRequestPasswordReset_decorators;
144
152
  let _accountsWhoami_decorators;
@@ -219,6 +227,12 @@ let QaSurface = (() => {
219
227
  let _pendingApprovals_decorators;
220
228
  let _answerApproval_decorators;
221
229
  let _pendingQuestions_decorators;
230
+ let _listForms_decorators;
231
+ let _getForm_decorators;
232
+ let _mutateForm_decorators;
233
+ let _validateFormStep_decorators;
234
+ let _formCapabilities_decorators;
235
+ let _readFormAttachment_decorators;
222
236
  let _answerQuestion_decorators;
223
237
  let _cancelQuestion_decorators;
224
238
  let _readSourceFile_decorators;
@@ -243,6 +257,9 @@ let QaSurface = (() => {
243
257
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
244
258
  _accountsRegister_decorators = [Remote("accountsRegister")];
245
259
  _accountsLogin_decorators = [Remote("accountsLogin")];
260
+ _accountsOidcStart_decorators = [Remote("accountsOidcStart")];
261
+ _accountsOidcComplete_decorators = [Remote("accountsOidcComplete")];
262
+ _accountsOidcLogout_decorators = [Remote("accountsOidcLogout")];
246
263
  _accountsChangePassword_decorators = [Remote("accountsChangePassword")];
247
264
  _accountsRequestPasswordReset_decorators = [Remote("accountsRequestPasswordReset")];
248
265
  _accountsWhoami_decorators = [Remote("accountsWhoami")];
@@ -323,6 +340,12 @@ let QaSurface = (() => {
323
340
  _pendingApprovals_decorators = [Remote("pendingApprovals")];
324
341
  _answerApproval_decorators = [Remote("answerApproval")];
325
342
  _pendingQuestions_decorators = [Remote("pendingQuestions")];
343
+ _listForms_decorators = [Remote("listForms")];
344
+ _getForm_decorators = [Remote("getForm")];
345
+ _mutateForm_decorators = [Remote("mutateForm")];
346
+ _validateFormStep_decorators = [Remote("validateFormStep")];
347
+ _formCapabilities_decorators = [Remote("formCapabilities")];
348
+ _readFormAttachment_decorators = [Remote("readFormAttachment")];
326
349
  _answerQuestion_decorators = [Remote("answerQuestion")];
327
350
  _cancelQuestion_decorators = [Remote("cancelQuestion")];
328
351
  _readSourceFile_decorators = [Remote("readSourceFile")];
@@ -344,6 +367,9 @@ let QaSurface = (() => {
344
367
  _slashExecute_decorators = [Remote("slashExecute")];
345
368
  __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);
346
369
  __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);
370
+ __esDecorate(this, null, _accountsOidcStart_decorators, { kind: "method", name: "accountsOidcStart", static: false, private: false, access: { has: obj => "accountsOidcStart" in obj, get: obj => obj.accountsOidcStart }, metadata: _metadata }, null, _instanceExtraInitializers);
371
+ __esDecorate(this, null, _accountsOidcComplete_decorators, { kind: "method", name: "accountsOidcComplete", static: false, private: false, access: { has: obj => "accountsOidcComplete" in obj, get: obj => obj.accountsOidcComplete }, metadata: _metadata }, null, _instanceExtraInitializers);
372
+ __esDecorate(this, null, _accountsOidcLogout_decorators, { kind: "method", name: "accountsOidcLogout", static: false, private: false, access: { has: obj => "accountsOidcLogout" in obj, get: obj => obj.accountsOidcLogout }, metadata: _metadata }, null, _instanceExtraInitializers);
347
373
  __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);
348
374
  __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);
349
375
  __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);
@@ -424,6 +450,12 @@ let QaSurface = (() => {
424
450
  __esDecorate(this, null, _pendingApprovals_decorators, { kind: "method", name: "pendingApprovals", static: false, private: false, access: { has: obj => "pendingApprovals" in obj, get: obj => obj.pendingApprovals }, metadata: _metadata }, null, _instanceExtraInitializers);
425
451
  __esDecorate(this, null, _answerApproval_decorators, { kind: "method", name: "answerApproval", static: false, private: false, access: { has: obj => "answerApproval" in obj, get: obj => obj.answerApproval }, metadata: _metadata }, null, _instanceExtraInitializers);
426
452
  __esDecorate(this, null, _pendingQuestions_decorators, { kind: "method", name: "pendingQuestions", static: false, private: false, access: { has: obj => "pendingQuestions" in obj, get: obj => obj.pendingQuestions }, metadata: _metadata }, null, _instanceExtraInitializers);
453
+ __esDecorate(this, null, _listForms_decorators, { kind: "method", name: "listForms", static: false, private: false, access: { has: obj => "listForms" in obj, get: obj => obj.listForms }, metadata: _metadata }, null, _instanceExtraInitializers);
454
+ __esDecorate(this, null, _getForm_decorators, { kind: "method", name: "getForm", static: false, private: false, access: { has: obj => "getForm" in obj, get: obj => obj.getForm }, metadata: _metadata }, null, _instanceExtraInitializers);
455
+ __esDecorate(this, null, _mutateForm_decorators, { kind: "method", name: "mutateForm", static: false, private: false, access: { has: obj => "mutateForm" in obj, get: obj => obj.mutateForm }, metadata: _metadata }, null, _instanceExtraInitializers);
456
+ __esDecorate(this, null, _validateFormStep_decorators, { kind: "method", name: "validateFormStep", static: false, private: false, access: { has: obj => "validateFormStep" in obj, get: obj => obj.validateFormStep }, metadata: _metadata }, null, _instanceExtraInitializers);
457
+ __esDecorate(this, null, _formCapabilities_decorators, { kind: "method", name: "formCapabilities", static: false, private: false, access: { has: obj => "formCapabilities" in obj, get: obj => obj.formCapabilities }, metadata: _metadata }, null, _instanceExtraInitializers);
458
+ __esDecorate(this, null, _readFormAttachment_decorators, { kind: "method", name: "readFormAttachment", static: false, private: false, access: { has: obj => "readFormAttachment" in obj, get: obj => obj.readFormAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
427
459
  __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);
428
460
  __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);
429
461
  __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);
@@ -453,6 +485,13 @@ let QaSurface = (() => {
453
485
  approvals;
454
486
  fileDeleteGate;
455
487
  userQuestions;
488
+ formService;
489
+ formGate;
490
+ formAttachments;
491
+ forms() {
492
+ return (this.formService ??= new QaFormService(new QaFormStore(path.join(path.dirname(defaultAccountsFilePath()), "qa-forms.db")), () => this.getConfig().forms, (session) => this.accountRemotes.resolve(this.getConfig())?.ownerIdOf(session) ??
493
+ session));
494
+ }
456
495
  provenance;
457
496
  notes;
458
497
  /** Assigned after the admission gate; that gate only reads it per execution. */
@@ -481,6 +520,12 @@ let QaSurface = (() => {
481
520
  integrationPrincipals = new QaIntegrationPrincipalBindings();
482
521
  webServer;
483
522
  disposeRoute;
523
+ oidc = new QaOidcService({
524
+ config: () => this.getConfig(),
525
+ accounts: () => this.accountRemotes.resolve(this.getConfig()),
526
+ });
527
+ disposeOidcRoute;
528
+ oidcRouteKey;
484
529
  routeKey;
485
530
  /**
486
531
  * The HTTP API an external application asks questions through. Constructed
@@ -498,6 +543,7 @@ let QaSurface = (() => {
498
543
  chatSharingStore;
499
544
  chatTransport;
500
545
  chatTurnGuard;
546
+ requestScheduler;
501
547
  sharing() {
502
548
  const accounts = () => {
503
549
  const store = this.accountRemotes.resolve(this.getConfig());
@@ -514,10 +560,13 @@ let QaSurface = (() => {
514
560
  // the one live source: each read folds its current snapshots, and a settings
515
561
  // write is visible on the next read without reloading the plugin.
516
562
  this.source = () => readConfigRefs(entry);
563
+ this.requestScheduler = new QaRequestScheduler(() => this.getConfig().session.maxActiveRequests, () => qaRequestRoots(ctx, this.getConfig().session.maxActiveRequests > 0), Date.now, (agent) => qaRequestFamilyBusy(ctx, agent));
564
+ ctx.effect(() => this.requestScheduler.install(ctx), "dsh-qa-surface.request-queue");
517
565
  this.logger = getPluginLogger({
518
566
  pluginId: "dsh-qa-surface",
519
567
  consoleSink: createHostLoggerSink(ctx.logger),
520
568
  });
569
+ this.formAttachments = new QaFormAttachmentAdapter(() => this.forms(), () => ctx.get("attachments"), (sessionId, file) => this.admission.grantAttachmentFile(sessionId, file));
521
570
  this.accountRemotes = createQaAccountRemotes({
522
571
  getConfig: () => this.getConfig(),
523
572
  logger: this.logger,
@@ -617,7 +666,16 @@ let QaSurface = (() => {
617
666
  // that is where the caller's account has just been matched against the
618
667
  // ownership record. The browser's `secureSession` remote keeps its own
619
668
  // write: there the compared identity is the token's.
620
- this.integrationPrincipals);
669
+ this.integrationPrincipals, {
670
+ readEvents: async (sessionId) => {
671
+ const result = await sessionLog.read(sessionId);
672
+ return result.ok ? result.events : [];
673
+ },
674
+ restoreFiles: async (sessionId) => {
675
+ if (this.getConfig().forms.enabled)
676
+ await this.formAttachments.restore(sessionId);
677
+ },
678
+ });
621
679
  this.provenance = new QaProvenanceHost(ctx, () => this.getConfig(),
622
680
  // Sharded per chat and bounded by the resolved retention policy: the
623
681
  // turn path must never rewrite history it did not touch.
@@ -632,7 +690,7 @@ let QaSurface = (() => {
632
690
  }, "dsh-qa-surface.chat-sharing");
633
691
  ctx.inject(["typertGateway", "connection"], () => {
634
692
  const dispose = installQaNativeFence(ctx, () => this.getConfig().accounts.enabled);
635
- const disposeUploads = installQaChatUploads(ctx, () => this.sharing(), (token, id) => this.secureSession(token, id));
693
+ const disposeUploads = installQaChatUploads(ctx, () => this.sharing(), (token, id) => this.secureSession(token, id), this.formAttachments);
636
694
  return async () => {
637
695
  dispose();
638
696
  await disposeUploads();
@@ -671,7 +729,20 @@ let QaSurface = (() => {
671
729
  this.approvals = new QaApprovalGate(ctx, () => this.getConfig().interaction.approvals === "interactive", ownership, this.logger);
672
730
  const integrationQuestionPolicy = new QaIntegrationQuestionPolicy(ctx, () => this.accountRemotes.resolve(this.getConfig()), (id) => ownership.rootOf(id));
673
731
  ctx.effect(() => () => integrationQuestionPolicy.dispose(), "qa-surface: integration question policy");
674
- this.userQuestions = new QaQuestionGate(ctx, () => this.getConfig().interaction.questions === "interactive", ownership, this.logger, (agent) => integrationQuestionPolicy.allowed(agent));
732
+ this.userQuestions = new QaQuestionGate(ctx, () => this.getConfig().interaction.questions === "interactive", ownership, this.logger, (agent) => integrationQuestionPolicy.allowed(agent) &&
733
+ !this.formGate?.hasSession(ownership.rootOf(String(agent.session.id)) ?? ""));
734
+ this.formGate = new QaFormGate(() => this.forms(), (id) => ownership.rootOf(id), (id) => this.userQuestions.list(id).length > 0 ||
735
+ this.approvals.list(id).length > 0, (exec) => this.getConfig().forms.enabled &&
736
+ exec.agent !== undefined &&
737
+ integrationQuestionPolicy.allowed(exec.agent) &&
738
+ this.admission.knowsSession(String(exec.agent.session.id)), () => this.getConfig().accounts.enabled &&
739
+ this.ctx.get("fileUploads") !== undefined &&
740
+ this.formAttachments.available());
741
+ this.formGate.install(ctx);
742
+ ctx.effect(() => () => {
743
+ this.formGate.dispose();
744
+ this.formService?.store.close();
745
+ }, "dsh-qa-surface.forms");
675
746
  // The destructive tool of the QA catalog composes the same approval flow
676
747
  // around itself: registered without `prepend`, it sits inside the approval
677
748
  // gate and answers `ask` for every file_delete call of an attested chat,
@@ -722,6 +793,9 @@ let QaSurface = (() => {
722
793
  // The QA tool catalog is attached per agent, never at boot: nothing here
723
794
  // reaches the model until a managed agent loads the activation skill.
724
795
  this.tools = new QaTools(ctx, {
796
+ forms: this.formGate,
797
+ formsEnabled: () => this.getConfig().forms.enabled,
798
+ formProjection: (submission, sessionId) => this.formAttachments.readable(submission, sessionId),
725
799
  logger: this.logger,
726
800
  dynamicActivation: this.getConfig().tools.dynamicActivation,
727
801
  activationSkill: this.getConfig().tools.activationSkill,
@@ -734,6 +808,12 @@ let QaSurface = (() => {
734
808
  docsDefaultVersion: () => docsDefaultVersionOf(this.getConfig().tools),
735
809
  });
736
810
  ctx.effect(() => () => this.tools.dispose(), "dsh-qa-surface.qa-tools");
811
+ const listenVolatile = ctx.on.bind(ctx);
812
+ ctx.effect(() => listenVolatile("loader/volatile-update", () => {
813
+ this.tools.refreshForms();
814
+ if (!this.getConfig().forms.enabled)
815
+ this.formGate.cancelAll();
816
+ }), "dsh-qa-surface.form-policy");
737
817
  // The root index gains one head script: non-loopback hostnames continue
738
818
  // into /qa, the loopback operator keeps the full harness UI. Not in the
739
819
  // kiosk composition: there the index itself is served under /qa, so the
@@ -760,6 +840,7 @@ let QaSurface = (() => {
760
840
  const config = this.getConfig();
761
841
  this.refreshRoute();
762
842
  this.refreshIntegrationRoutes();
843
+ this.refreshOidcRoute();
763
844
  this.warnDocumentsMoved();
764
845
  this.warnLegacySlashDefaults();
765
846
  this.logger.info("config.updated", {
@@ -772,11 +853,16 @@ let QaSurface = (() => {
772
853
  this.webServer = webContext.webServer;
773
854
  this.refreshRoute();
774
855
  this.refreshIntegrationRoutes();
856
+ this.refreshOidcRoute();
775
857
  webContext.effect(() => () => {
776
858
  this.disposeRoute?.();
777
859
  this.disposeRoute = undefined;
778
860
  this.routeKey = undefined;
779
861
  this.disposeIntegrationRoutes?.();
862
+ this.disposeOidcRoute?.();
863
+ this.disposeOidcRoute = undefined;
864
+ this.oidcRouteKey = undefined;
865
+ this.oidc.clear();
780
866
  this.disposeIntegrationRoutes = undefined;
781
867
  this.integrationRouteKey = undefined;
782
868
  this.webServer = undefined;
@@ -827,6 +913,10 @@ let QaSurface = (() => {
827
913
  * cannot be opened must not fail the page that triggered it.
828
914
  */
829
915
  dropVanishedChats(sessionIds) {
916
+ for (const id of sessionIds) {
917
+ this.formGate?.cancel(id);
918
+ this.formService?.store.cancelSession(id);
919
+ }
830
920
  try {
831
921
  const rows = this.quality().dropConversations(sessionIds);
832
922
  this.logger.info("quality.vanished-conversations-dropped", {
@@ -927,6 +1017,17 @@ let QaSurface = (() => {
927
1017
  accountsLogin(email, password) {
928
1018
  return this.accountRemotes.login(email, password);
929
1019
  }
1020
+ accountsOidcStart() {
1021
+ if (!this.disposeOidcRoute)
1022
+ return Promise.reject(oidcRefusal());
1023
+ return this.oidc.start();
1024
+ }
1025
+ accountsOidcComplete(handoff, completionKey) {
1026
+ return this.oidc.complete(handoff, completionKey);
1027
+ }
1028
+ accountsOidcLogout(token) {
1029
+ return this.oidc.logout(token);
1030
+ }
930
1031
  /** Identity probe; safe to call with an empty or expired token. */
931
1032
  accountsChangePassword(token, currentPassword, nextPassword) {
932
1033
  return this.accountRemotes.changePassword(token, currentPassword, nextPassword);
@@ -1028,20 +1129,15 @@ let QaSurface = (() => {
1028
1129
  return this.getConfig();
1029
1130
  }
1030
1131
  /**
1031
- * Report how much of the deployment's request ceiling the stand is using, so
1032
- * a browser holds its question back instead of opening another turn on a
1033
- * model that is busy answering the ones it allows.
1034
- *
1035
- * The count is the Host's, not the browser's: a visitor cannot see another
1036
- * account's chats, and the HTTP API's questions are invisible to every chat
1037
- * view. Read-only, and a ceiling rather than a lock — a prompt rides the
1038
- * native session RPC this plugin does not own, so the boundary is the
1039
- * browser's own courtesy, and two questions sent in the same instant can
1040
- * still overshoot by one. What the ceiling buys is that steady state, not a
1041
- * strict bound.
1132
+ * Report server admission. Global reads expose counts only; a chat's FIFO
1133
+ * position requires that account's current read access to the chat. The
1134
+ * scheduler gates every root turn, including native and integration sends.
1042
1135
  */
1043
- queueStatus() {
1044
- return qaQueueStatus(this.getConfig().session.maxActiveRequests, qaActiveRequests(this.ctx.agents.roots()));
1136
+ queueStatus(token, sessionId) {
1137
+ if (sessionId !== undefined && this.getConfig().accounts.enabled) {
1138
+ this.sharing().access(token ?? "", sessionId);
1139
+ }
1140
+ return this.requestScheduler.status(sessionId);
1045
1141
  }
1046
1142
  /**
1047
1143
  * Create a QA session from Host-owned inputs. With account workspaces on,
@@ -1050,65 +1146,26 @@ let QaSurface = (() => {
1050
1146
  */
1051
1147
  async createSession(token, subroleId, adminPreview) {
1052
1148
  const config = this.getConfig();
1053
- if (config.session.policy === "fixed") {
1054
- throw new Error("Fixed QA sessions cannot be created.");
1055
- }
1056
- const id = SessionId(`session-${randomUUID()}`);
1057
1149
  const accounts = this.accountRemotes.resolve(config);
1058
- const owner = accounts === undefined
1059
- ? undefined
1060
- : this.accountRemotes.run(() => this.access.reserveSession(token, String(id), subroleId, adminPreview));
1061
- try {
1062
- // Check deployment-only pins before creating a durable Host session.
1063
- // Full admission below still verifies the composed agent and tool view.
1064
- this.admission.preflightDeployment();
1065
- let userCwd;
1066
- if (config.accounts.perUserWorkspace) {
1067
- const workspaceId = config.session.workspaceId;
1068
- if (owner === undefined || workspaceId === null) {
1069
- throw new Error("Per-user QA workspace configuration is unavailable.");
1070
- }
1071
- const workspace = this.ctx.workspaceRegistry.get(WorkspaceId(workspaceId));
1072
- if (workspace === undefined) {
1073
- throw new Error(`Configured QA workspace ${workspaceId} is unavailable.`);
1074
- }
1075
- userCwd = prepareQaUserWorkspace(workspace.path, owner.id);
1076
- }
1077
- const created = await this.ctx.sessionController.create({
1078
- sessionId: id,
1079
- ...(userCwd !== undefined
1080
- ? { cwd: userCwd }
1081
- : config.session.workspaceId !== null
1082
- ? { workspaceId: WorkspaceId(config.session.workspaceId) }
1083
- : config.session.cwd !== null
1084
- ? { cwd: config.session.cwd }
1085
- : {}),
1086
- ...(config.session.agentPreset === null
1087
- ? {}
1088
- : { agentPreset: config.session.agentPreset }),
1089
- });
1090
- await applySessionModelPolicy(this.ctx, created.sessionId, this.access.modelPolicyFor(String(created.sessionId)), this.logger);
1091
- const sessionId = String(created.sessionId);
1092
- await this.admission.secureSession(token, sessionId);
1093
- this.integrationPrincipals.attest(sessionId, owner?.id, accounts?.ownerIdOf(sessionId));
1094
- return sessionId;
1095
- }
1096
- catch (error) {
1097
- // This call is the only way the browser could have learned the id, so a
1098
- // failure at any stage — the Host creating the session, selecting the
1099
- // model, the admission refusing the composition — leaves no chat behind
1100
- // and the reservation goes with it. A kept record would put an empty
1101
- // chat in the account's list, and nothing else would ever remove it.
1102
- if (owner !== undefined) {
1103
- accounts?.releaseSessionReservation(owner.id, String(id));
1104
- }
1105
- this.logger.error("session.create-rejected", {
1106
- sessionId: String(id),
1107
- reason: error instanceof QaAttestationError ? error.reason : undefined,
1108
- error: error instanceof Error ? error.message : String(error),
1109
- });
1110
- throw new Error(qaAttestationFailureMessage("Unable to create a QA session.", error), { cause: error });
1111
- }
1150
+ return createQaSession({
1151
+ ctx: this.ctx,
1152
+ config,
1153
+ logger: this.logger,
1154
+ sessionFiles: createSessionEraser(),
1155
+ reserve: (id) => accounts === undefined
1156
+ ? undefined
1157
+ : this.accountRemotes.run(() => this.access.reserveSession(token, id, subroleId, adminPreview)),
1158
+ release: (owner, id) => accounts?.releaseSessionReservation(owner, id),
1159
+ modelPolicy: (id) => this.access.modelPolicyFor(id),
1160
+ preflight: () => this.admission.preflightDeployment(),
1161
+ admit: (id) => this.admission.secureSession(token, id),
1162
+ attest: (id, owner) => this.integrationPrincipals.attest(id, owner, accounts?.ownerIdOf(id)),
1163
+ reject: (id) => {
1164
+ this.integrationPrincipals.attest(id, undefined, undefined);
1165
+ this.admission.rejectSession(id);
1166
+ },
1167
+ forget: (id) => this.admission.forgetRejectedSession(id),
1168
+ });
1112
1169
  }
1113
1170
  /** Create an independently admitted ordinary branch of the caller's own chat. */
1114
1171
  async forkSession(token, sourceId, answerSeq) {
@@ -1406,7 +1463,9 @@ let QaSurface = (() => {
1406
1463
  }
1407
1464
  async chatAudit(token, sessionId, full) {
1408
1465
  this.requireChatOwner(token, sessionId);
1409
- await this.secureSession(token, sessionId);
1466
+ // Sidebar summaries read stored audit metadata; they do not need an agent.
1467
+ if (full)
1468
+ await this.secureSession(token, sessionId);
1410
1469
  this.requireChatOwner(token, sessionId);
1411
1470
  const audit = this.ctx.get("sessionAudit");
1412
1471
  if (audit === undefined)
@@ -1523,6 +1582,115 @@ let QaSurface = (() => {
1523
1582
  return [];
1524
1583
  return this.userQuestions.list(sessionId);
1525
1584
  }
1585
+ /** Forms stay owner-scoped; UUIDs and a browser-supplied session never grant access. */
1586
+ async admitForm(token, sessionId) {
1587
+ try {
1588
+ this.requireChatOwner(token, sessionId);
1589
+ await this.secureSession(token, sessionId);
1590
+ this.requireChatOwner(token, sessionId);
1591
+ }
1592
+ catch {
1593
+ throw new QaFormError("ACCESS_DENIED", "Form access is unavailable.");
1594
+ }
1595
+ }
1596
+ async listForms(token, sessionId) {
1597
+ try {
1598
+ await this.admitForm(token, sessionId);
1599
+ if (!this.getConfig().forms.enabled)
1600
+ return { ok: true, value: [] };
1601
+ this.formGate.touch(sessionId);
1602
+ const service = this.forms();
1603
+ return {
1604
+ ok: true,
1605
+ value: service.store
1606
+ .list(sessionId)
1607
+ .map((r) => service.view(r, this.formGate.waiting(r.id))),
1608
+ };
1609
+ }
1610
+ catch (error) {
1611
+ return formFailure(error);
1612
+ }
1613
+ }
1614
+ async getForm(token, sessionId, formId) {
1615
+ try {
1616
+ await this.admitForm(token, sessionId);
1617
+ const s = this.forms();
1618
+ return {
1619
+ ok: true,
1620
+ value: s.view(s.get(sessionId, formId), this.formGate.waiting(formId)),
1621
+ };
1622
+ }
1623
+ catch (error) {
1624
+ return formFailure(error);
1625
+ }
1626
+ }
1627
+ async mutateForm(token, sessionId, command) {
1628
+ try {
1629
+ await this.admitForm(token, sessionId);
1630
+ const s = this.forms(), r = s.command(sessionId, command);
1631
+ this.logger.info("form.mutated", {
1632
+ sessionId,
1633
+ formId: r.id,
1634
+ action: command.action,
1635
+ revision: r.revision,
1636
+ status: r.status,
1637
+ });
1638
+ this.formGate.notify(r, command.action === "pause");
1639
+ return { ok: true, value: s.view(r, this.formGate.waiting(r.id)) };
1640
+ }
1641
+ catch (error) {
1642
+ return formFailure(error);
1643
+ }
1644
+ }
1645
+ async validateFormStep(token, sessionId, formId, stepId, revision) {
1646
+ try {
1647
+ await this.admitForm(token, sessionId);
1648
+ this.forms().step(sessionId, formId, stepId, revision);
1649
+ return { ok: true, value: null };
1650
+ }
1651
+ catch (error) {
1652
+ return formFailure(error);
1653
+ }
1654
+ }
1655
+ async formCapabilities() {
1656
+ return {
1657
+ enabled: this.getConfig().forms.enabled,
1658
+ schemaVersion: 1,
1659
+ upload: this.getConfig().accounts.enabled &&
1660
+ this.ctx.get("fileUploads") !== undefined &&
1661
+ this.formAttachments.available(),
1662
+ };
1663
+ }
1664
+ async readFormAttachment(token, sessionId, formId, resourceId) {
1665
+ try {
1666
+ await this.admitForm(token, sessionId);
1667
+ const s = this.forms(), r = s.get(sessionId, formId);
1668
+ const file = Object.values(r.attachments)
1669
+ .flat()
1670
+ .find((f) => f.resource_id === resourceId);
1671
+ if (!file)
1672
+ return {
1673
+ ok: false,
1674
+ error: {
1675
+ code: "ACCESS_DENIED",
1676
+ message: "File access is unavailable.",
1677
+ },
1678
+ };
1679
+ const data = s.store.resource(formId, resourceId);
1680
+ this.requireChatOwner(token, sessionId);
1681
+ return {
1682
+ ok: true,
1683
+ value: {
1684
+ name: file.name,
1685
+ media_type: file.media_type,
1686
+ data: Buffer.from(data).toString("base64"),
1687
+ },
1688
+ };
1689
+ }
1690
+ catch (error) {
1691
+ return formFailure(error);
1692
+ }
1693
+ }
1526
1694
  /**
1527
1695
  * Apply the operator's answers to one parked question request. Every question
1528
1696
  * of the request must be present in `answers`; one the browser omits reads as
@@ -1901,6 +2069,28 @@ let QaSurface = (() => {
1901
2069
  });
1902
2070
  }
1903
2071
  }
2072
+ refreshOidcRoute() {
2073
+ const config = this.getConfig();
2074
+ const key = config.enabled && config.accounts.enabled && config.accounts.oidc.enabled
2075
+ ? JSON.stringify([config.accounts.oidc, config.route.path])
2076
+ : undefined;
2077
+ if (key === this.oidcRouteKey)
2078
+ return;
2079
+ this.disposeOidcRoute?.();
2080
+ this.disposeOidcRoute = undefined;
2081
+ this.oidcRouteKey = undefined;
2082
+ this.oidc.clear();
2083
+ if (!key || !this.webServer)
2084
+ return;
2085
+ try {
2086
+ this.disposeOidcRoute = registerQaOidcRoute(this.webServer, config, this.oidc);
2087
+ this.oidcRouteKey = key;
2088
+ this.logger.info("oidc.route-registered", {});
2089
+ }
2090
+ catch {
2091
+ this.logger.error("oidc.route-unavailable", {});
2092
+ }
2093
+ }
1904
2094
  refreshRoute() {
1905
2095
  const config = this.getConfig();
1906
2096
  // Kiosk: the harness route policy owns the QA paths and serves the