@yadsh/dsh-qa-surface 0.17.0 → 0.18.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 (185) hide show
  1. package/README.md +15 -3
  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/chat-sharing/native-fence.js +20 -0
  21. package/lib/chat-sharing/native-fence.js.map +1 -1
  22. package/lib/chat-sharing/transport.js +8 -2
  23. package/lib/chat-sharing/transport.js.map +1 -1
  24. package/lib/chat-sharing/uploads.js +79 -6
  25. package/lib/chat-sharing/uploads.js.map +1 -1
  26. package/lib/cli.js +13 -0
  27. package/lib/cli.js.map +1 -1
  28. package/lib/client/QaAccountsController.js +61 -0
  29. package/lib/client/QaAccountsController.js.map +1 -1
  30. package/lib/client/QaRouteController.js +3 -1
  31. package/lib/client/QaRouteController.js.map +1 -1
  32. package/lib/client/QaSessionController.js +90 -26
  33. package/lib/client/QaSessionController.js.map +1 -1
  34. package/lib/client/QaSurface.js +40 -11
  35. package/lib/client/QaSurface.js.map +1 -1
  36. package/lib/client/account-sessions.js +74 -8
  37. package/lib/client/account-sessions.js.map +1 -1
  38. package/lib/client/admin/CapabilityPicker.js +50 -0
  39. package/lib/client/admin/CapabilityPicker.js.map +1 -0
  40. package/lib/client/admin/CoverageNotice.js +9 -0
  41. package/lib/client/admin/CoverageNotice.js.map +1 -0
  42. package/lib/client/admin/QaAdmin.js +3 -150
  43. package/lib/client/admin/QaAdmin.js.map +1 -1
  44. package/lib/client/admin/RoleEditor.js +46 -0
  45. package/lib/client/admin/RoleEditor.js.map +1 -0
  46. package/lib/client/admin/capability-selection.js +63 -0
  47. package/lib/client/admin/capability-selection.js.map +1 -0
  48. package/lib/client/admin/pages/Conversations.js +35 -17
  49. package/lib/client/admin/pages/Conversations.js.map +1 -1
  50. package/lib/client/admin/pages/Overview.js +3 -2
  51. package/lib/client/admin/pages/Overview.js.map +1 -1
  52. package/lib/client/admin/pages/Quality.js +77 -20
  53. package/lib/client/admin/pages/Quality.js.map +1 -1
  54. package/lib/client/admin/pages/SkillFiles.js +14 -3
  55. package/lib/client/admin/pages/SkillFiles.js.map +1 -1
  56. package/lib/client/admin/shared.js +68 -3
  57. package/lib/client/admin/shared.js.map +1 -1
  58. package/lib/client/components/QaAuthGate.js +31 -28
  59. package/lib/client/components/QaAuthGate.js.map +1 -1
  60. package/lib/client/components/QaChangelog.js +44 -0
  61. package/lib/client/components/QaChangelog.js.map +1 -1
  62. package/lib/client/components/QaComposer.js +27 -4
  63. package/lib/client/components/QaComposer.js.map +1 -1
  64. package/lib/client/components/QaFormItem.js +63 -0
  65. package/lib/client/components/QaFormItem.js.map +1 -0
  66. package/lib/client/components/QaInputForm.js +281 -0
  67. package/lib/client/components/QaInputForm.js.map +1 -0
  68. package/lib/client/components/QaModal.js +17 -4
  69. package/lib/client/components/QaModal.js.map +1 -1
  70. package/lib/client/forms-styles.js +14 -0
  71. package/lib/client/forms-styles.js.map +1 -0
  72. package/lib/client/forms.js +40 -0
  73. package/lib/client/forms.js.map +1 -0
  74. package/lib/client/index.js +15 -1
  75. package/lib/client/index.js.map +1 -1
  76. package/lib/client/oidc-browser.js +39 -0
  77. package/lib/client/oidc-browser.js.map +1 -0
  78. package/lib/client/panels/PanelHost.js +23 -1
  79. package/lib/client/panels/PanelHost.js.map +1 -1
  80. package/lib/client/settings/sections/interface.js +2 -1
  81. package/lib/client/settings/sections/interface.js.map +1 -1
  82. package/lib/client/settings/sections/status.js +3 -1
  83. package/lib/client/settings/sections/status.js.map +1 -1
  84. package/lib/client/styles.js +3 -3
  85. package/lib/client/types.js.map +1 -1
  86. package/lib/client.js +465 -72918
  87. package/lib/client.js.map +1 -1
  88. package/lib/config-resolvers/accounts.js +5 -4
  89. package/lib/config-resolvers/accounts.js.map +1 -1
  90. package/lib/config-resolvers/defaults.js +16 -0
  91. package/lib/config-resolvers/defaults.js.map +1 -1
  92. package/lib/config-resolvers/oidc.js +53 -0
  93. package/lib/config-resolvers/oidc.js.map +1 -0
  94. package/lib/config.js +71 -0
  95. package/lib/config.js.map +1 -1
  96. package/lib/forms/attachments.js +214 -0
  97. package/lib/forms/attachments.js.map +1 -0
  98. package/lib/forms/errors.js +37 -0
  99. package/lib/forms/errors.js.map +1 -0
  100. package/lib/forms/fingerprint.js +21 -0
  101. package/lib/forms/fingerprint.js.map +1 -0
  102. package/lib/forms/gate.js +125 -0
  103. package/lib/forms/gate.js.map +1 -0
  104. package/lib/forms/schema.js +182 -0
  105. package/lib/forms/schema.js.map +1 -0
  106. package/lib/forms/service.js +220 -0
  107. package/lib/forms/service.js.map +1 -0
  108. package/lib/forms/store.js +194 -0
  109. package/lib/forms/store.js.map +1 -0
  110. package/lib/forms/types.js +2 -0
  111. package/lib/forms/types.js.map +1 -0
  112. package/lib/forms/validation.js +113 -0
  113. package/lib/forms/validation.js.map +1 -0
  114. package/lib/index.js +227 -3
  115. package/lib/index.js.map +1 -1
  116. package/lib/oidc/http.js +30 -0
  117. package/lib/oidc/http.js.map +1 -0
  118. package/lib/oidc/service.js +250 -0
  119. package/lib/oidc/service.js.map +1 -0
  120. package/lib/qa-tools/activation-manager.js +24 -2
  121. package/lib/qa-tools/activation-manager.js.map +1 -1
  122. package/lib/qa-tools/catalog.js +11 -1
  123. package/lib/qa-tools/catalog.js.map +1 -1
  124. package/lib/qa-tools/index.js +27 -0
  125. package/lib/qa-tools/index.js.map +1 -1
  126. package/lib/qa-tools/request-user-form.js +121 -0
  127. package/lib/qa-tools/request-user-form.js.map +1 -0
  128. package/lib/resolve-config.js +2 -0
  129. package/lib/resolve-config.js.map +1 -1
  130. package/lib/secure-session.js +14 -8
  131. package/lib/secure-session.js.map +1 -1
  132. package/lib/typert.host.js +4587 -1101
  133. package/lib/typert.remote-client.d.ts +133 -3
  134. package/lib/typert.remote-client.js +4463 -1103
  135. package/lib/types/accounts/database.d.ts +8 -0
  136. package/lib/types/accounts/file.d.ts +2 -0
  137. package/lib/types/accounts/store.d.ts +17 -0
  138. package/lib/types/accounts/token.d.ts +3 -1
  139. package/lib/types/admin/metrics.d.ts +5 -1
  140. package/lib/types/admin/read-budget.d.ts +8 -0
  141. package/lib/types/admin/service.d.ts +17 -0
  142. package/lib/types/admin/session-log.d.ts +30 -1
  143. package/lib/types/chat-sharing/uploads.d.ts +2 -1
  144. package/lib/types/client/QaAccountsController.d.ts +4 -0
  145. package/lib/types/client/QaRouteController.d.ts +1 -0
  146. package/lib/types/client/QaSessionController.d.ts +12 -3
  147. package/lib/types/client/QaSurface.d.ts +2 -0
  148. package/lib/types/client/admin/CapabilityPicker.d.ts +14 -0
  149. package/lib/types/client/admin/CoverageNotice.d.ts +6 -0
  150. package/lib/types/client/admin/RoleEditor.d.ts +15 -0
  151. package/lib/types/client/admin/capability-selection.d.ts +27 -0
  152. package/lib/types/client/admin/shared.d.ts +39 -1
  153. package/lib/types/client/components/QaAuthGate.d.ts +2 -0
  154. package/lib/types/client/components/QaFormItem.d.ts +20 -0
  155. package/lib/types/client/components/QaInputForm.d.ts +18 -0
  156. package/lib/types/client/forms-styles.d.ts +2 -0
  157. package/lib/types/client/forms.d.ts +23 -0
  158. package/lib/types/client/oidc-browser.d.ts +12 -0
  159. package/lib/types/client/types.d.ts +7 -1
  160. package/lib/types/config-resolvers/oidc.d.ts +3 -0
  161. package/lib/types/forms/attachments.d.ts +48 -0
  162. package/lib/types/forms/errors.d.ts +11 -0
  163. package/lib/types/forms/fingerprint.d.ts +9 -0
  164. package/lib/types/forms/gate.d.ts +33 -0
  165. package/lib/types/forms/schema.d.ts +72 -0
  166. package/lib/types/forms/service.d.ts +15 -0
  167. package/lib/types/forms/store.d.ts +25 -0
  168. package/lib/types/forms/types.d.ts +215 -0
  169. package/lib/types/forms/validation.d.ts +20 -0
  170. package/lib/types/index.d.ts +28 -0
  171. package/lib/types/oidc/http.d.ts +5 -0
  172. package/lib/types/oidc/service.d.ts +30 -0
  173. package/lib/types/oidc.js +2 -0
  174. package/lib/types/oidc.js.map +1 -0
  175. package/lib/types/qa-tools/activation-manager.d.ts +5 -0
  176. package/lib/types/qa-tools/catalog.d.ts +4 -1
  177. package/lib/types/qa-tools/index.d.ts +5 -1
  178. package/lib/types/qa-tools/request-user-form.d.ts +5 -0
  179. package/lib/types/secure-session.d.ts +1 -0
  180. package/lib/types/types/accounts.d.ts +2 -0
  181. package/lib/types/types/admin.d.ts +6 -0
  182. package/lib/types/types/config.d.ts +6 -0
  183. package/lib/types/types/oidc.d.ts +36 -0
  184. package/lib/types/types.d.ts +2 -0
  185. package/package.json +57 -55
@@ -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
@@ -142,6 +150,9 @@ export declare class QaSurface extends TypertRemoteService {
142
150
  */
143
151
  accountsRegister(email: string, password: string): QaAccountSession;
144
152
  accountsLogin(email: string, password: string): QaAccountSession;
153
+ accountsOidcStart(): Promise<import("./types.js").QaOidcStart>;
154
+ accountsOidcComplete(handoff: string, completionKey: string): QaAccountSession;
155
+ accountsOidcLogout(token: string): Promise<string | null>;
145
156
  /** Identity probe; safe to call with an empty or expired token. */
146
157
  accountsChangePassword(token: string, currentPassword: string, nextPassword: string): QaAccountSession;
147
158
  /**
@@ -391,6 +402,22 @@ export declare class QaSurface extends TypertRemoteService {
391
402
  * deployment with unsupported questions answers with an empty list.
392
403
  */
393
404
  pendingQuestions(token: string, sessionId: string): Promise<readonly QaPendingQuestion[]>;
405
+ /** Forms stay owner-scoped; UUIDs and a browser-supplied session never grant access. */
406
+ private admitForm;
407
+ listForms(token: string, sessionId: string): Promise<QaFormResult<QaFormView[]>>;
408
+ getForm(token: string, sessionId: string, formId: string): Promise<QaFormResult<QaFormView>>;
409
+ mutateForm(token: string, sessionId: string, command: QaFormCommand): Promise<QaFormResult<QaFormView>>;
410
+ validateFormStep(token: string, sessionId: string, formId: string, stepId: string, revision: number): Promise<QaFormResult<null>>;
411
+ formCapabilities(): Promise<{
412
+ enabled: boolean;
413
+ schemaVersion: number;
414
+ upload: boolean;
415
+ }>;
416
+ readFormAttachment(token: string, sessionId: string, formId: string, resourceId: string): Promise<QaFormResult<{
417
+ name: string;
418
+ media_type: string;
419
+ data: string;
420
+ }>>;
394
421
  /**
395
422
  * Apply the operator's answers to one parked question request. Every question
396
423
  * of the request must be present in `answers`; one the browser omits reads as
@@ -511,6 +538,7 @@ export declare class QaSurface extends TypertRemoteService {
511
538
  * immediately rather than at the next restart.
512
539
  */
513
540
  private refreshIntegrationRoutes;
541
+ private refreshOidcRoute;
514
542
  private refreshRoute;
515
543
  /**
516
544
  * 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":""}
@@ -19,7 +19,12 @@ export interface QaToolActivationManagerOptions {
19
19
  export declare class QaToolActivationManager implements QaToolActivation {
20
20
  private readonly options;
21
21
  private readonly states;
22
+ private readonly activeAgents;
23
+ private catalog;
22
24
  constructor(options: QaToolActivationManagerOptions);
25
+ /** Reconcile feature switches only for scopes which already earned activation. */
26
+ replaceCatalog(catalog: readonly QaToolDescriptor[]): void;
27
+ disposeAll(): void;
23
28
  /**
24
29
  * Register the whole catalog on one agent, all or nothing.
25
30
  *
@@ -1,5 +1,6 @@
1
1
  import type { Agent } from "@deepseek-ai/dsh-agent";
2
2
  import type { QaToolDescriptor } from "./types.js";
3
+ import type { QaFormGate } from "../forms/gate.js";
3
4
  /**
4
5
  * Deterministic catalog identity. The durable activation marker is derived from
5
6
  * the session log rather than stored, so this version is not compared against a
@@ -8,9 +9,11 @@ import type { QaToolDescriptor } from "./types.js";
8
9
  *
9
10
  * Bump it whenever the catalog's tool set or their schemas change.
10
11
  */
11
- export declare const QA_TOOL_CATALOG_VERSION = "3";
12
+ export declare const QA_TOOL_CATALOG_VERSION = "4";
12
13
  /** What the catalog's definitions read, beyond the execution record. */
13
14
  export interface QaToolsSelfcheckDeps {
15
+ readonly forms?: QaFormGate;
16
+ readonly formProjection?: (submission: import("../forms/types.js").QaFormSubmission) => Promise<unknown>;
14
17
  readonly catalogVersion: string;
15
18
  readonly activationMode: string;
16
19
  /** Names the catalog contributes, in catalog order. */
@@ -3,6 +3,9 @@ import type { Context } from "@deepseek-ai/cordis";
3
3
  import type { PluginLogger } from "@yadsh/dsh-plugin-log";
4
4
  import type { QaToolActivation } from "./types.js";
5
5
  export interface QaToolsOptions {
6
+ readonly forms?: import("../forms/gate.js").QaFormGate;
7
+ readonly formsEnabled?: () => boolean;
8
+ readonly formProjection?: (submission: import("../forms/types.js").QaFormSubmission) => Promise<unknown>;
6
9
  readonly logger: PluginLogger;
7
10
  readonly dynamicActivation: boolean;
8
11
  readonly activationSkill: string;
@@ -31,12 +34,13 @@ export declare class QaTools implements QaToolActivation {
31
34
  private readonly manager;
32
35
  private readonly detector;
33
36
  private readonly lifecycle;
34
- private readonly catalogTools;
37
+ private catalogTools;
35
38
  constructor(ctx: Context, options: QaToolsOptions);
36
39
  /** Names currently registered on one agent; the admission guard reads this per call. */
37
40
  activeToolNames(agent: Agent): readonly string[];
38
41
  /** Shipped scope-local names, including entries not activated on an agent yet. */
39
42
  catalogToolNames(): readonly string[];
43
+ refreshForms(): void;
40
44
  dispose(): void;
41
45
  /**
42
46
  * The preset gate. An empty `activationPresets` leaves it open, which is only
@@ -0,0 +1,5 @@
1
+ import type { ToolDefinition } from "@deepseek-ai/dsh-tools";
2
+ import type { QaFormGate } from "../forms/gate.js";
3
+ /** Build only; the QA activation manager owns registration and role admission. */
4
+ export declare function createRequestUserFormTool(gate: QaFormGate, project?: (submission: import("../forms/types.js").QaFormSubmission) => Promise<unknown>): ToolDefinition;
5
+ //# sourceMappingURL=request-user-form.d.ts.map
@@ -71,6 +71,7 @@ export declare class QaPolicyAdmission {
71
71
  * one set answers for the whole chat.
72
72
  */
73
73
  private readonly attested;
74
+ private readonly questionWarnings;
74
75
  private readonly workspaceAccess;
75
76
  /**
76
77
  * Removers of the document input roots granted per session, so a grant lives
@@ -1,6 +1,8 @@
1
1
  import type { QaAccountRole } from "./policy.js";
2
2
  /** The account fields projected to browsers; never includes credentials. */
3
3
  export interface QaAccountUserPublic {
4
+ /** Corporate passwords are managed outside QA. Absent on legacy/local accounts. */
5
+ readonly authMethod?: "oidc";
4
6
  readonly id: string;
5
7
  readonly email: string;
6
8
  readonly displayName: string;
@@ -196,6 +196,12 @@ export interface QaQualityTrendPoint {
196
196
  readonly negative: number;
197
197
  }
198
198
  export interface QaQualityMetrics {
199
+ /** Counts derived from journals are incomplete until every journal was read. */
200
+ readonly coverage?: {
201
+ readonly complete: boolean;
202
+ readonly readConversations: number;
203
+ readonly totalConversations: number;
204
+ };
199
205
  readonly conversations: number;
200
206
  readonly activeUsers: number;
201
207
  readonly assistantMessages: number;
@@ -94,6 +94,7 @@ export interface QaNotesConfig {
94
94
  };
95
95
  }
96
96
  export interface QaSurfaceConfig {
97
+ readonly forms?: Partial<import("../forms/types.js").QaFormPolicy>;
97
98
  readonly enabled?: boolean;
98
99
  readonly route?: {
99
100
  readonly path?: string;
@@ -237,6 +238,8 @@ export interface QaSurfaceConfig {
237
238
  readonly frameAncestors?: string | null;
238
239
  };
239
240
  readonly accounts?: {
241
+ readonly localLogin?: boolean;
242
+ readonly oidc?: import("./oidc.js").QaOidcConfig;
240
243
  /** Opt-in; the QA gate stays off until the deployment turns this on. */
241
244
  readonly enabled?: boolean;
242
245
  readonly allowRegistration?: boolean;
@@ -372,6 +375,7 @@ export interface QaAttachmentsConfig {
372
375
  readonly extensions?: readonly string[];
373
376
  }
374
377
  export interface ResolvedQaSurfaceConfig {
378
+ readonly forms: Readonly<import("../forms/types.js").QaFormPolicy>;
375
379
  readonly enabled: boolean;
376
380
  readonly route: {
377
381
  readonly path: string;
@@ -441,6 +445,8 @@ export interface ResolvedQaSurfaceConfig {
441
445
  readonly frameAncestors: string | null;
442
446
  };
443
447
  readonly accounts: {
448
+ readonly localLogin: boolean;
449
+ readonly oidc: import("./oidc.js").ResolvedQaOidcConfig;
444
450
  readonly enabled: boolean;
445
451
  readonly allowRegistration: boolean;
446
452
  readonly sessionTtlDays: number;