@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
@@ -5,7 +5,10 @@ import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountSessi
5
5
  /** Wire content one prompt carries: text plus base64 image uploads. */
6
6
  export type QaPromptContent = Parameters<SessionFace["prompt"]>[0];
7
7
  /** DSH session runtime surface the QA controller is allowed to touch. */
8
- export type QaSessions = ISessions;
8
+ export type QaSessions = ISessions & {
9
+ /** Register an exact id admitted by authenticated QA create/read before retain. */
10
+ rememberAdmitted?(sessionId: string): void;
11
+ };
9
12
  /** Narrow client API slice used to pin the preset and model of a fresh chat. */
10
13
  export type QaSessionsApi = {
11
14
  selectModel: ClientRemote["session"]["selectModel"];
@@ -34,10 +37,10 @@ export type QaCreateSession = (token: string, subroleId: string | null, adminPre
34
37
  export type QaForkSession = (token: string, sourceId: string, answerSeq: number) => Promise<RemoteResult<string>>;
35
38
  /**
36
39
  * The Host's live read of the request ceiling (`qaSurface/queueStatus`), asked
37
- * per send: how many questions the stand is answering and whether one more
38
- * fits.
40
+ * per send and for the current waiting chat. Supplying a session id requires
41
+ * current account read access; global reads expose counts only.
39
42
  */
40
- export type QaQueueStatusRemote = () => Promise<RemoteResult<QaQueueStatus>>;
43
+ export type QaQueueStatusRemote = (token?: string, sessionId?: string) => Promise<RemoteResult<QaQueueStatus>>;
41
44
  /** Role selector plus administrator mutation channel. */
42
45
  export interface QaAccessApi {
43
46
  current(token: string): Promise<RemoteResult<QaCurrentAccess>>;
@@ -351,6 +354,9 @@ export interface QaAccountsApi {
351
354
  accountsSetServiceTokenClarifications?(token: string, tokenId: string, allowed: boolean): Promise<RemoteResult<{
352
355
  readonly updated: boolean;
353
356
  }>>;
357
+ accountsOidcStart?(): Promise<RemoteResult<import("../types.js").QaOidcStart>>;
358
+ accountsOidcComplete?(handoff: string, completionKey: string): Promise<RemoteResult<QaAccountSession>>;
359
+ accountsOidcLogout?(token: string): Promise<RemoteResult<string | null>>;
354
360
  chatVisible?(token: string): Promise<RemoteResult<readonly import("../chat-sharing/types.js").QaChatAccess[]>>;
355
361
  accountsWhoami(token: string): Promise<RemoteResult<QaWhoamiResult>>;
356
362
  accountsLogin(email: string, password: string): Promise<RemoteResult<QaAccountSession>>;
@@ -0,0 +1,3 @@
1
+ import type { QaSurfaceConfig, ResolvedQaOidcConfig } from "../types.js";
2
+ export declare function resolveOidc(input: QaSurfaceConfig): ResolvedQaOidcConfig;
3
+ //# sourceMappingURL=oidc.d.ts.map
@@ -0,0 +1,12 @@
1
+ /** The Host tool contract, normalized once for path and storage policy. */
2
+ export type QaFilesystemIntent = {
3
+ readonly kind: "read";
4
+ readonly path: string;
5
+ readonly fileOnlyGrant: boolean;
6
+ } | {
7
+ readonly kind: "write";
8
+ readonly path: string;
9
+ readonly bytes: number;
10
+ };
11
+ export declare function qaFilesystemIntent(name: string, args: Record<string, unknown> | undefined): QaFilesystemIntent | undefined;
12
+ //# sourceMappingURL=filesystem-intent.d.ts.map
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import type { QaFormService } from "./service.js";
3
+ import type { QaFormRecord, QaFormSubmission } from "./types.js";
4
+ declare const fileRef: z.ZodObject<{
5
+ attachmentId: z.ZodString;
6
+ name: z.ZodString;
7
+ bytes: z.ZodNumber;
8
+ }, z.core.$strip>;
9
+ type FileRef = z.infer<typeof fileRef>;
10
+ /** Structural port to the installed Host; receipt data is runtime-validated. */
11
+ export interface QaFormHostAttachments {
12
+ readonly root: string;
13
+ readFileStream(file: FileRef, signal?: AbortSignal): AsyncIterable<Uint8Array>;
14
+ saveFile(input: {
15
+ data: Uint8Array;
16
+ name: string;
17
+ }): Promise<unknown>;
18
+ fileHostPath(file: FileRef): string | undefined;
19
+ validateImage?(input: {
20
+ data: Uint8Array;
21
+ mediaType: "image/png" | "image/jpeg";
22
+ name: string;
23
+ }): Promise<void>;
24
+ }
25
+ export declare function validateFormFile(name: string, data: Buffer): string;
26
+ /** Durable bytes belong to the form store; Host objects are verified reader projections. */
27
+ export declare class QaFormAttachmentAdapter {
28
+ private readonly service;
29
+ private readonly host;
30
+ private readonly grantFile?;
31
+ constructor(service: () => QaFormService, host: () => QaFormHostAttachments | undefined, grantFile?: ((sessionId: string, file: string) => void) | undefined);
32
+ available(): boolean;
33
+ check(session: string, formId: string, uploadId: string): void;
34
+ claim(session: string, uploadId: string): {
35
+ record: QaFormRecord;
36
+ upload: QaFormRecord["uploads"][number];
37
+ };
38
+ complete(session: string, formId: string, uploadId: string, raw: unknown, signal: AbortSignal, authorize: () => void): Promise<QaFormRecord>;
39
+ fail(session: string, formId: string, uploadId: string): void;
40
+ /** Exact saved bytes are re-projected through the Host on each successful resume. */
41
+ readable(submission: QaFormSubmission, sessionId?: string): Promise<QaFormSubmission & {
42
+ resources: {
43
+ resource_id: string;
44
+ read_path: string;
45
+ }[];
46
+ }>;
47
+ /** Rebuild exact grants from this chat's persisted, verified submissions. */
48
+ restore(sessionId: string): Promise<void>;
49
+ }
50
+ export {};
51
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1,11 @@
1
+ import type { QaFormFieldError, QaFormResult } from "./types.js";
2
+ export declare class QaFormError extends Error {
3
+ readonly code: string;
4
+ readonly revision?: number | undefined;
5
+ readonly fields?: QaFormFieldError[] | undefined;
6
+ constructor(code: string, message: string, revision?: number | undefined, fields?: QaFormFieldError[] | undefined);
7
+ }
8
+ /** Deliberately redact unexpected storage/Host exceptions at the wire boundary. */
9
+ export declare function formFailure(error: unknown): QaFormResult<never>;
10
+ export declare function formAssert(condition: unknown, code: string, message: string): asserts condition;
11
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stable object-key ordering for schema and idempotency fingerprints.
3
+ *
4
+ * Lives apart from the sqlite store so every store implementation hashes the
5
+ * same way without importing the sqlite bridge: the client test graph mounts a
6
+ * store too, and a browser environment must not see `@yadsh/dsh-plugin-kit/sqlite`.
7
+ */
8
+ export declare function fingerprint(value: unknown): string;
9
+ //# sourceMappingURL=fingerprint.d.ts.map
@@ -0,0 +1,33 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { ToolExecution } from "@deepseek-ai/dsh-tools";
3
+ import type { QaFormService } from "./service.js";
4
+ import type { QaFormRecord, QaFormSubmission } from "./types.js";
5
+ type Outcome = QaFormSubmission | {
6
+ status: "paused";
7
+ form_id: string;
8
+ reason: string;
9
+ };
10
+ /** A parked tool call is transient. Never reconstruct one from a saved draft. */
11
+ export declare class QaFormGate {
12
+ readonly service: () => QaFormService;
13
+ private readonly rootOf;
14
+ private readonly busy;
15
+ private readonly allowed;
16
+ private readonly filesAvailable;
17
+ private readonly waiters;
18
+ private readonly disposers;
19
+ private closed;
20
+ private readonly clients;
21
+ constructor(service: () => QaFormService, rootOf: (id: string) => string | undefined, busy: (id: string) => boolean, allowed: (exec: ToolExecution) => boolean, filesAvailable?: () => boolean);
22
+ install(ctx: Context): void;
23
+ waiting(id: string): boolean;
24
+ touch(session: string): void;
25
+ hasSession(session: string): boolean;
26
+ request(raw: unknown, exec: ToolExecution): Promise<Outcome>;
27
+ notify(record: QaFormRecord, pause?: boolean): void;
28
+ cancel(session: string): void;
29
+ cancelAll(): void;
30
+ dispose(): void;
31
+ }
32
+ export {};
33
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import type { QaFormPolicy, QaFormSchema } from "./types.js";
3
+ export declare const DEFAULT_FORM_POLICY: Readonly<QaFormPolicy>;
4
+ export declare const formSchemaParser: z.ZodObject<{
5
+ schema_version: z.ZodLiteral<1>;
6
+ title: z.ZodString;
7
+ description: z.ZodOptional<z.ZodString>;
8
+ submit_label: z.ZodOptional<z.ZodString>;
9
+ steps: z.ZodArray<z.ZodObject<{
10
+ id: z.ZodString;
11
+ title: z.ZodString;
12
+ description: z.ZodOptional<z.ZodString>;
13
+ items: z.ZodArray<z.ZodObject<{
14
+ id: z.ZodString;
15
+ title: z.ZodString;
16
+ description: z.ZodOptional<z.ZodString>;
17
+ answer: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
18
+ type: z.ZodLiteral<"text">;
19
+ required: z.ZodBoolean;
20
+ placeholder: z.ZodOptional<z.ZodString>;
21
+ min_length: z.ZodOptional<z.ZodNumber>;
22
+ max_length: z.ZodOptional<z.ZodNumber>;
23
+ }, z.core.$strict>, z.ZodObject<{
24
+ type: z.ZodLiteral<"textarea">;
25
+ required: z.ZodBoolean;
26
+ placeholder: z.ZodOptional<z.ZodString>;
27
+ min_length: z.ZodOptional<z.ZodNumber>;
28
+ max_length: z.ZodOptional<z.ZodNumber>;
29
+ }, z.core.$strict>, z.ZodObject<{
30
+ type: z.ZodLiteral<"single_select">;
31
+ required: z.ZodBoolean;
32
+ options: z.ZodArray<z.ZodObject<{
33
+ value: z.ZodString;
34
+ label: z.ZodString;
35
+ description: z.ZodOptional<z.ZodString>;
36
+ recommended: z.ZodOptional<z.ZodBoolean>;
37
+ }, z.core.$strict>>;
38
+ allow_other: z.ZodOptional<z.ZodBoolean>;
39
+ }, z.core.$strict>, z.ZodObject<{
40
+ type: z.ZodLiteral<"multi_select">;
41
+ required: z.ZodBoolean;
42
+ options: z.ZodArray<z.ZodObject<{
43
+ value: z.ZodString;
44
+ label: z.ZodString;
45
+ description: z.ZodOptional<z.ZodString>;
46
+ recommended: z.ZodOptional<z.ZodBoolean>;
47
+ }, z.core.$strict>>;
48
+ allow_other: z.ZodOptional<z.ZodBoolean>;
49
+ min_selected: z.ZodOptional<z.ZodNumber>;
50
+ max_selected: z.ZodOptional<z.ZodNumber>;
51
+ }, z.core.$strict>], "type">>;
52
+ files: z.ZodDiscriminatedUnion<[z.ZodObject<{
53
+ requirement: z.ZodLiteral<"none">;
54
+ }, z.core.$strict>, z.ZodObject<{
55
+ max_count: z.ZodNumber;
56
+ accept: z.ZodArray<z.ZodString>;
57
+ max_file_size_bytes: z.ZodNumber;
58
+ requirement: z.ZodLiteral<"required">;
59
+ min_count: z.ZodNumber;
60
+ }, z.core.$strict>, z.ZodObject<{
61
+ max_count: z.ZodNumber;
62
+ accept: z.ZodArray<z.ZodString>;
63
+ max_file_size_bytes: z.ZodNumber;
64
+ requirement: z.ZodLiteral<"optional">;
65
+ }, z.core.$strict>], "requirement">;
66
+ }, z.core.$strict>>;
67
+ }, z.core.$strict>>;
68
+ }, z.core.$strict>;
69
+ /** Shared by server and browser. No Node APIs or browser-supplied readiness. */
70
+ export declare function parseFormSchema(input: unknown, policy: Readonly<QaFormPolicy>): QaFormSchema;
71
+ export declare function resolveFormPolicy(input?: Partial<QaFormPolicy>): QaFormPolicy;
72
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,15 @@
1
+ import type { QaFormPolicy, QaFormRecord, QaFormStoreApi, QaFormView } from "./types.js";
2
+ export declare class QaFormService {
3
+ readonly store: QaFormStoreApi;
4
+ readonly policy: () => Readonly<QaFormPolicy>;
5
+ private readonly ownerOf;
6
+ constructor(store: QaFormStoreApi, policy: () => Readonly<QaFormPolicy>, ownerOf?: (session: string) => string);
7
+ enabled(): void;
8
+ create(session: string, key: string, input: unknown): QaFormRecord;
9
+ view(record: QaFormRecord, waiting?: boolean): QaFormView;
10
+ get(session: string, id: string): QaFormRecord;
11
+ step(session: string, id: string, stepId: string, revision: number): void;
12
+ command(session: string, raw: unknown): QaFormRecord;
13
+ private beginUpload;
14
+ }
15
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { SqliteDatabase } from "@yadsh/dsh-plugin-kit/sqlite";
2
+ import type { QaFormRecord, QaFormStoreApi } from "./types.js";
3
+ export { fingerprint } from "./fingerprint.js";
4
+ /** Full synchronous commits: snapshot, revision and mutation receipt share one transaction. */
5
+ export declare class QaFormStore implements QaFormStoreApi {
6
+ readonly storage: SqliteDatabase;
7
+ constructor(file: string);
8
+ list(session: string): QaFormRecord[];
9
+ get(session: string, id: string): QaFormRecord;
10
+ put(record: QaFormRecord): void;
11
+ create(session: string, key: string, schema: QaFormRecord["schema"], creator?: string): QaFormRecord;
12
+ editableOrSubmitted(record: QaFormRecord): void;
13
+ private mutationReceipt;
14
+ replayedMutation(formId: string, mutationId: string, input: unknown): QaFormRecord | null;
15
+ mutate(session: string, id: string, revision: number, mutationId: string, input: unknown, work: (r: QaFormRecord) => void): QaFormRecord;
16
+ /** Async work happens before this short final transaction; no revision captured at upload start. */
17
+ update(session: string, id: string, work: (r: QaFormRecord) => void): QaFormRecord;
18
+ resource(formId: string, id: string): Uint8Array;
19
+ saveResource(formId: string, id: string, data: Uint8Array): void;
20
+ storedBytes(session: string): number;
21
+ identityReservedBytes(creator: string): number;
22
+ cancelSession(session: string): void;
23
+ close(): void;
24
+ }
25
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,215 @@
1
+ /** Browser-safe wire contract; storage and tool waiting have independent lifetimes. */
2
+ export interface QaFormPolicy {
3
+ enabled: boolean;
4
+ maxSteps: number;
5
+ maxItemsPerForm: number;
6
+ maxOptionsPerItem: number;
7
+ maxSchemaBytes: number;
8
+ maxTextLength: number;
9
+ maxTotalAnswerBytes: number;
10
+ maxFilesPerItem: number;
11
+ maxFileSizeBytes: number;
12
+ maxTotalFileBytesPerForm: number;
13
+ maxConcurrentUploadsPerSession: number;
14
+ uploadIntentTtlSeconds: number;
15
+ allowedExtensions: readonly string[];
16
+ }
17
+ export interface QaFormOption {
18
+ value: string;
19
+ label: string;
20
+ description?: string | undefined;
21
+ recommended?: boolean | undefined;
22
+ }
23
+ export type QaFormAnswerSpec = {
24
+ type: "text" | "textarea";
25
+ required: boolean;
26
+ placeholder?: string | undefined;
27
+ min_length?: number | undefined;
28
+ max_length?: number | undefined;
29
+ } | {
30
+ type: "single_select" | "multi_select";
31
+ required: boolean;
32
+ options: QaFormOption[];
33
+ allow_other?: boolean | undefined;
34
+ min_selected?: number | undefined;
35
+ max_selected?: number | undefined;
36
+ };
37
+ export type QaFormFilesSpec = {
38
+ requirement: "none";
39
+ } | {
40
+ requirement: "required";
41
+ min_count: number;
42
+ max_count: number;
43
+ accept: string[];
44
+ max_file_size_bytes: number;
45
+ } | {
46
+ requirement: "optional";
47
+ max_count: number;
48
+ accept: string[];
49
+ max_file_size_bytes: number;
50
+ };
51
+ export interface QaFormItem {
52
+ id: string;
53
+ title: string;
54
+ description?: string | undefined;
55
+ answer?: QaFormAnswerSpec | undefined;
56
+ files: QaFormFilesSpec;
57
+ }
58
+ export interface QaFormStep {
59
+ id: string;
60
+ title: string;
61
+ description?: string | undefined;
62
+ items: QaFormItem[];
63
+ }
64
+ export interface QaFormSchema {
65
+ schema_version: 1;
66
+ title: string;
67
+ description?: string | undefined;
68
+ submit_label?: string | undefined;
69
+ steps: QaFormStep[];
70
+ }
71
+ export type QaFormAnswer = {
72
+ type: "text" | "textarea";
73
+ text: string;
74
+ } | {
75
+ type: "single_select" | "multi_select";
76
+ selected: string[];
77
+ other_text?: string | undefined;
78
+ };
79
+ export interface QaFormAttachment {
80
+ attachment_id: string;
81
+ version: number;
82
+ name: string;
83
+ size_bytes: number;
84
+ media_type: string;
85
+ sha256: string;
86
+ resource_id: string;
87
+ }
88
+ export interface QaFormUpload {
89
+ id: string;
90
+ item_id: string;
91
+ name: string;
92
+ size_bytes: number;
93
+ state: "pending" | "uploading" | "failed" | "ready" | "cancelled";
94
+ expires_at: number;
95
+ target?: {
96
+ attachment_id: string;
97
+ version: number;
98
+ } | undefined;
99
+ result?: QaFormAttachment;
100
+ error?: string;
101
+ }
102
+ export interface QaFormFieldError {
103
+ step_id: string;
104
+ item_id: string;
105
+ component: "answer" | "files";
106
+ code: string;
107
+ message: string;
108
+ }
109
+ export interface QaFormSubmission {
110
+ status: "submitted";
111
+ form_id: string;
112
+ submission_id: string;
113
+ schema_version: 1;
114
+ schema_hash: string;
115
+ form_revision: number;
116
+ submitted_at: string;
117
+ items: {
118
+ step_id: string;
119
+ item_id: string;
120
+ answer: QaFormAnswer | null;
121
+ files: QaFormAttachment[];
122
+ }[];
123
+ empty_optional_item_ids: string[];
124
+ }
125
+ export interface QaFormRecord {
126
+ storage_version: 1;
127
+ id: string;
128
+ session_id: string;
129
+ creator_id: string;
130
+ request_key: string;
131
+ schema: QaFormSchema;
132
+ schema_hash: string;
133
+ status: "draft" | "submitted" | "cancelled" | "expired";
134
+ revision: number;
135
+ created_at: string;
136
+ answers: Record<string, QaFormAnswer | null>;
137
+ attachments: Record<string, QaFormAttachment[]>;
138
+ uploads: QaFormUpload[];
139
+ step: number;
140
+ submission?: QaFormSubmission;
141
+ }
142
+ export type QaFormView = QaFormRecord & {
143
+ waiting: boolean;
144
+ field_errors: QaFormFieldError[];
145
+ };
146
+ export interface QaFormMutation {
147
+ form_id: string;
148
+ expected_revision: number;
149
+ mutation_id: string;
150
+ }
151
+ export type QaFormCommand = (QaFormMutation & {
152
+ action: "save";
153
+ answers: Record<string, QaFormAnswer | null>;
154
+ step: number;
155
+ }) | (QaFormMutation & {
156
+ action: "submit" | "pause";
157
+ }) | (QaFormMutation & {
158
+ action: "begin_upload";
159
+ item_id: string;
160
+ name: string;
161
+ size_bytes: number;
162
+ target?: {
163
+ attachment_id: string;
164
+ version: number;
165
+ } | undefined;
166
+ }) | (QaFormMutation & {
167
+ action: "cancel_upload";
168
+ upload_id: string;
169
+ }) | (QaFormMutation & {
170
+ action: "remove";
171
+ item_id: string;
172
+ attachment_id: string;
173
+ version: number;
174
+ });
175
+ export type QaFormResult<T> = {
176
+ ok: true;
177
+ value: T;
178
+ } | {
179
+ ok: false;
180
+ error: {
181
+ code: string;
182
+ message: string;
183
+ form_revision?: number;
184
+ field_errors?: QaFormFieldError[];
185
+ };
186
+ };
187
+ /**
188
+ * The store surface the service programs against.
189
+ *
190
+ * The service must never reach into a concrete storage backend: the shipped
191
+ * store is sqlite (durable), and the client component tests mount an in-memory
192
+ * implementation, which a browser test environment could never load through
193
+ * the sqlite bridge.
194
+ */
195
+ export interface QaFormStoreApi {
196
+ list(session: string): QaFormRecord[];
197
+ create(session: string, key: string, schema: QaFormRecord["schema"], creator?: string): QaFormRecord;
198
+ get(session: string, id: string): QaFormRecord;
199
+ editableOrSubmitted(record: QaFormRecord): void;
200
+ mutate(session: string, id: string, revision: number, mutationId: string, input: unknown, work: (r: QaFormRecord) => void): QaFormRecord;
201
+ update(session: string, id: string, work: (r: QaFormRecord) => void): QaFormRecord;
202
+ resource(formId: string, id: string): Uint8Array;
203
+ saveResource(formId: string, id: string, data: Uint8Array): void;
204
+ storedBytes(session: string): number;
205
+ identityReservedBytes(creator: string): number;
206
+ cancelSession(session: string): void;
207
+ /**
208
+ * Submit retries are idempotent: when this mutation identity was already
209
+ * applied, return its recorded result and refuse a reused identity that
210
+ * carries different data. `null` means the identity is unused.
211
+ */
212
+ replayedMutation(formId: string, mutationId: string, input: unknown): QaFormRecord | null;
213
+ close(): void;
214
+ }
215
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ import type { QaFormAnswer, QaFormAnswerSpec, QaFormFieldError, QaFormPolicy, QaFormRecord } from "./types.js";
3
+ export declare const formAnswerParser: z.ZodUnion<readonly [z.ZodObject<{
4
+ type: z.ZodEnum<{
5
+ text: "text";
6
+ textarea: "textarea";
7
+ }>;
8
+ text: z.ZodString;
9
+ }, z.core.$strict>, z.ZodObject<{
10
+ type: z.ZodEnum<{
11
+ multi_select: "multi_select";
12
+ single_select: "single_select";
13
+ }>;
14
+ selected: z.ZodArray<z.ZodString>;
15
+ other_text: z.ZodOptional<z.ZodString>;
16
+ }, z.core.$strict>]>;
17
+ /** Requiredness is checked at navigation/submit; invalid supplied values fail even if optional. */
18
+ export declare function normalizeFormAnswer(spec: QaFormAnswerSpec | undefined, raw: unknown, policy: Readonly<QaFormPolicy>, requireComplete?: boolean): QaFormAnswer | null;
19
+ export declare function validateForm(record: QaFormRecord, policy: Readonly<QaFormPolicy>, stepId?: string): QaFormFieldError[];
20
+ //# sourceMappingURL=validation.d.ts.map
@@ -3,6 +3,7 @@ import type { SessionPageRequest, SessionFollowRequest, SessionPromptRequest, Se
3
3
  import { TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
4
4
  import type { Context } from "@deepseek-ai/cordis";
5
5
  import type { QaSurfaceConfigRefs } from "./config.js";
6
+ import type { QaFormCommand, QaFormResult, QaFormView } from "./forms/types.js";
6
7
  import type { QaAccountNotificationsInput } from "./types.js";
7
8
  import type { QaTurnSources } from "./provenance/types.js";
8
9
  import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaApprovalDecision, QaClaimResult, QaDocumentPreview, QaSpreadsheetPreviewSelection, QaDocumentWorkflowStatus, QaIssuedServiceToken, QaLockdownProof, QaOwnershipEntry, QaPasswordResetRequest, QaPendingApproval, QaPendingQuestion, QaQuestionAnswerItem, QaQueueStatus, QaServiceTokenCreateInput, QaServiceTokenSummary, ResolvedQaSurfaceConfig, QaAdminSkillScope, QaAdminSkillsView, QaExpertMemoryDraft, QaExpertMemoryPage, QaExpertMemoryRecord, QaExpertMemoryScope, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillSummary, QaSkillToolDescriptor, QaSkillValidation, QaSourceFilePreview, QaWhoamiResult, QaWorkspaceFile, QaWorkspaceFileChunk, QaWorkspaceListing, QaPrincipal, QaAccessAdminSnapshot, QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaCapabilitySelection, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaCurrentAccess, QaFeedbackHarvestEntry, QaFeedbackHarvestResult, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaModelCatalogEntry, QaModelPair, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaSessionAccess, QaSlashCatalog, QaSlashExecution, QaSlashSubmitAttachment, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, QaUserQuery } from "./types.js";
@@ -26,6 +27,10 @@ export declare class QaSurface extends TypertRemoteService {
26
27
  private readonly approvals;
27
28
  private readonly fileDeleteGate;
28
29
  private readonly userQuestions;
30
+ private formService?;
31
+ private readonly formGate;
32
+ private readonly formAttachments;
33
+ private forms;
29
34
  private readonly provenance;
30
35
  private readonly notes;
31
36
  /** Assigned after the admission gate; that gate only reads it per execution. */
@@ -54,6 +59,9 @@ export declare class QaSurface extends TypertRemoteService {
54
59
  private readonly integrationPrincipals;
55
60
  private webServer;
56
61
  private disposeRoute;
62
+ private readonly oidc;
63
+ private disposeOidcRoute;
64
+ private oidcRouteKey;
57
65
  private routeKey;
58
66
  /**
59
67
  * The HTTP API an external application asks questions through. Constructed
@@ -71,6 +79,7 @@ export declare class QaSurface extends TypertRemoteService {
71
79
  private chatSharingStore;
72
80
  private readonly chatTransport;
73
81
  private readonly chatTurnGuard;
82
+ private readonly requestScheduler;
74
83
  private sharing;
75
84
  constructor(ctx: Context, entry?: Partial<QaSurfaceConfigRefs>);
76
85
  /** The quality store, opening its file the first time anyone needs it. */
@@ -142,6 +151,9 @@ export declare class QaSurface extends TypertRemoteService {
142
151
  */
143
152
  accountsRegister(email: string, password: string): QaAccountSession;
144
153
  accountsLogin(email: string, password: string): QaAccountSession;
154
+ accountsOidcStart(): Promise<import("./types.js").QaOidcStart>;
155
+ accountsOidcComplete(handoff: string, completionKey: string): QaAccountSession;
156
+ accountsOidcLogout(token: string): Promise<string | null>;
145
157
  /** Identity probe; safe to call with an empty or expired token. */
146
158
  accountsChangePassword(token: string, currentPassword: string, nextPassword: string): QaAccountSession;
147
159
  /**
@@ -226,19 +238,11 @@ export declare class QaSurface extends TypertRemoteService {
226
238
  */
227
239
  describe(): ResolvedQaSurfaceConfig;
228
240
  /**
229
- * Report how much of the deployment's request ceiling the stand is using, so
230
- * a browser holds its question back instead of opening another turn on a
231
- * model that is busy answering the ones it allows.
232
- *
233
- * The count is the Host's, not the browser's: a visitor cannot see another
234
- * account's chats, and the HTTP API's questions are invisible to every chat
235
- * view. Read-only, and a ceiling rather than a lock — a prompt rides the
236
- * native session RPC this plugin does not own, so the boundary is the
237
- * browser's own courtesy, and two questions sent in the same instant can
238
- * still overshoot by one. What the ceiling buys is that steady state, not a
239
- * strict bound.
241
+ * Report server admission. Global reads expose counts only; a chat's FIFO
242
+ * position requires that account's current read access to the chat. The
243
+ * scheduler gates every root turn, including native and integration sends.
240
244
  */
241
- queueStatus(): QaQueueStatus;
245
+ queueStatus(token?: string, sessionId?: string): QaQueueStatus;
242
246
  /**
243
247
  * Create a QA session from Host-owned inputs. With account workspaces on,
244
248
  * cwd is `<configured workspace>/.qa-users/<account UUID>` and the child is
@@ -391,6 +395,22 @@ export declare class QaSurface extends TypertRemoteService {
391
395
  * deployment with unsupported questions answers with an empty list.
392
396
  */
393
397
  pendingQuestions(token: string, sessionId: string): Promise<readonly QaPendingQuestion[]>;
398
+ /** Forms stay owner-scoped; UUIDs and a browser-supplied session never grant access. */
399
+ private admitForm;
400
+ listForms(token: string, sessionId: string): Promise<QaFormResult<QaFormView[]>>;
401
+ getForm(token: string, sessionId: string, formId: string): Promise<QaFormResult<QaFormView>>;
402
+ mutateForm(token: string, sessionId: string, command: QaFormCommand): Promise<QaFormResult<QaFormView>>;
403
+ validateFormStep(token: string, sessionId: string, formId: string, stepId: string, revision: number): Promise<QaFormResult<null>>;
404
+ formCapabilities(): Promise<{
405
+ enabled: boolean;
406
+ schemaVersion: number;
407
+ upload: boolean;
408
+ }>;
409
+ readFormAttachment(token: string, sessionId: string, formId: string, resourceId: string): Promise<QaFormResult<{
410
+ name: string;
411
+ media_type: string;
412
+ data: string;
413
+ }>>;
394
414
  /**
395
415
  * Apply the operator's answers to one parked question request. Every question
396
416
  * of the request must be present in `answers`; one the browser omits reads as
@@ -511,6 +531,7 @@ export declare class QaSurface extends TypertRemoteService {
511
531
  * immediately rather than at the next restart.
512
532
  */
513
533
  private refreshIntegrationRoutes;
534
+ private refreshOidcRoute;
514
535
  private refreshRoute;
515
536
  /**
516
537
  * A deployment that set `lockdown.allowSlashCommands: true` before this
@@ -0,0 +1,5 @@
1
+ import type { WebServer } from "@deepseek-ai/dsh-host-webserver";
2
+ import type { ResolvedQaSurfaceConfig } from "../types.js";
3
+ import type { QaOidcService } from "./service.js";
4
+ export declare function registerQaOidcRoute(webServer: Pick<WebServer, "register">, config: ResolvedQaSurfaceConfig, service: QaOidcService): () => void;
5
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1,30 @@
1
+ import * as oidc from "openid-client";
2
+ import type { QaAccounts } from "../accounts/store.js";
3
+ import type { QaAccountSession, QaOidcStart, ResolvedQaSurfaceConfig } from "../types.js";
4
+ /** A coarse refusal never carries provider responses, codes or tokens. */
5
+ export declare function oidcRefusal(): Error;
6
+ /** Authorization Code + PKCE with a browser-bound, single-use QA handoff. */
7
+ export declare class QaOidcService {
8
+ private readonly options;
9
+ private generation;
10
+ private key;
11
+ private discovery;
12
+ private readonly transactions;
13
+ private readonly handoffs;
14
+ private attempts;
15
+ constructor(options: {
16
+ config: () => ResolvedQaSurfaceConfig;
17
+ accounts: () => QaAccounts | undefined;
18
+ discover?: typeof oidc.discovery;
19
+ });
20
+ private sync;
21
+ clear(): void;
22
+ private prune;
23
+ private client;
24
+ start(): Promise<QaOidcStart>;
25
+ /** Callback is reconstructed from configured origin, never Host/Forwarded headers. */
26
+ callback(requestUrl: string): Promise<string>;
27
+ complete(handoff: string, completionKey: string): QaAccountSession;
28
+ logout(token: string): Promise<string | null>;
29
+ }
30
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/types/oidc.ts"],"names":[],"mappings":""}