@yadsh/dsh-qa-surface 0.19.0 → 0.20.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 (216) hide show
  1. package/README.md +181 -0
  2. package/lib/access/model-choices.js +103 -0
  3. package/lib/access/model-choices.js.map +1 -0
  4. package/lib/access/model-policy.js +79 -7
  5. package/lib/access/model-policy.js.map +1 -1
  6. package/lib/access/model.js +29 -1
  7. package/lib/access/model.js.map +1 -1
  8. package/lib/access/queue-policy.js +36 -0
  9. package/lib/access/queue-policy.js.map +1 -0
  10. package/lib/access/service.js +124 -7
  11. package/lib/access/service.js.map +1 -1
  12. package/lib/account-remotes.js +10 -1
  13. package/lib/account-remotes.js.map +1 -1
  14. package/lib/accounts/database.js +44 -3
  15. package/lib/accounts/database.js.map +1 -1
  16. package/lib/accounts/store.js +28 -0
  17. package/lib/accounts/store.js.map +1 -1
  18. package/lib/admin/service.js +6 -1
  19. package/lib/admin/service.js.map +1 -1
  20. package/lib/chat-sharing/transport.js +6 -2
  21. package/lib/chat-sharing/transport.js.map +1 -1
  22. package/lib/client/QaAccountsController.js +43 -8
  23. package/lib/client/QaAccountsController.js.map +1 -1
  24. package/lib/client/QaSessionController.js +55 -23
  25. package/lib/client/QaSessionController.js.map +1 -1
  26. package/lib/client/QaSurface.js +61 -3
  27. package/lib/client/QaSurface.js.map +1 -1
  28. package/lib/client/admin/ModelPolicyEditor.js +28 -2
  29. package/lib/client/admin/ModelPolicyEditor.js.map +1 -1
  30. package/lib/client/admin/RoleEditor.js +18 -1
  31. package/lib/client/admin/RoleEditor.js.map +1 -1
  32. package/lib/client/admin/pages/SkillFiles.js +1 -1
  33. package/lib/client/admin/pages/SkillFiles.js.map +1 -1
  34. package/lib/client/admin/pages/Users.js +36 -16
  35. package/lib/client/admin/pages/Users.js.map +1 -1
  36. package/lib/client/admin/styles.js +3 -0
  37. package/lib/client/admin/styles.js.map +1 -1
  38. package/lib/client/code-source-styles.js +12 -0
  39. package/lib/client/code-source-styles.js.map +1 -0
  40. package/lib/client/components/QaChangelog.js +69 -0
  41. package/lib/client/components/QaChangelog.js.map +1 -1
  42. package/lib/client/components/QaChatActions.js +36 -14
  43. package/lib/client/components/QaChatActions.js.map +1 -1
  44. package/lib/client/components/QaCodeSourcePicker.js +18 -0
  45. package/lib/client/components/QaCodeSourcePicker.js.map +1 -0
  46. package/lib/client/components/QaComposer.js +11 -12
  47. package/lib/client/components/QaComposer.js.map +1 -1
  48. package/lib/client/components/QaComposerSettings.js +202 -0
  49. package/lib/client/components/QaComposerSettings.js.map +1 -0
  50. package/lib/client/components/QaFormItem.js +74 -38
  51. package/lib/client/components/QaFormItem.js.map +1 -1
  52. package/lib/client/components/QaHistoryCatalog.js +261 -0
  53. package/lib/client/components/QaHistoryCatalog.js.map +1 -0
  54. package/lib/client/components/QaInputForm.js +115 -32
  55. package/lib/client/components/QaInputForm.js.map +1 -1
  56. package/lib/client/components/QaModelSelector.js +21 -0
  57. package/lib/client/components/QaModelSelector.js.map +1 -0
  58. package/lib/client/components/QaSidebar.js +25 -24
  59. package/lib/client/components/QaSidebar.js.map +1 -1
  60. package/lib/client/composer-settings-styles.js +21 -0
  61. package/lib/client/composer-settings-styles.js.map +1 -0
  62. package/lib/client/forms-styles.js +35 -11
  63. package/lib/client/forms-styles.js.map +1 -1
  64. package/lib/client/history-organization-styles.js +31 -0
  65. package/lib/client/history-organization-styles.js.map +1 -0
  66. package/lib/client/history-organization.js +155 -0
  67. package/lib/client/history-organization.js.map +1 -0
  68. package/lib/client/index.js +13 -4
  69. package/lib/client/index.js.map +1 -1
  70. package/lib/client/settings/sections/attachments.js +7 -1
  71. package/lib/client/settings/sections/attachments.js.map +1 -1
  72. package/lib/client/settings/sections/session.js +1 -1
  73. package/lib/client/settings/sections/session.js.map +1 -1
  74. package/lib/client/styles.js +18 -1
  75. package/lib/client/styles.js.map +1 -1
  76. package/lib/client/types.js.map +1 -1
  77. package/lib/client/use-chat-code-source.js +156 -0
  78. package/lib/client/use-chat-code-source.js.map +1 -0
  79. package/lib/client/use-history-organization.js +48 -0
  80. package/lib/client/use-history-organization.js.map +1 -0
  81. package/lib/client/use-model-selection.js +96 -0
  82. package/lib/client/use-model-selection.js.map +1 -0
  83. package/lib/client/use-qa-file-drop.js +81 -0
  84. package/lib/client/use-qa-file-drop.js.map +1 -0
  85. package/lib/client/user-settings/IntegrationTokensPage.js +42 -6
  86. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -1
  87. package/lib/client/user-settings/UserSettingsDialog.js +1 -1
  88. package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
  89. package/lib/client.js +123 -25
  90. package/lib/client.js.map +1 -1
  91. package/lib/code-source-selection.js +102 -0
  92. package/lib/code-source-selection.js.map +1 -0
  93. package/lib/config-resolvers/accounts.js +26 -1
  94. package/lib/config-resolvers/accounts.js.map +1 -1
  95. package/lib/config-resolvers/defaults.js +5 -0
  96. package/lib/config-resolvers/defaults.js.map +1 -1
  97. package/lib/config-resolvers/integration.js +8 -1
  98. package/lib/config-resolvers/integration.js.map +1 -1
  99. package/lib/config-resolvers/session.js +2 -0
  100. package/lib/config-resolvers/session.js.map +1 -1
  101. package/lib/config.js +51 -3
  102. package/lib/config.js.map +1 -1
  103. package/lib/enforcement/skill-policy.js +37 -2
  104. package/lib/enforcement/skill-policy.js.map +1 -1
  105. package/lib/forms/errors.js +6 -1
  106. package/lib/forms/errors.js.map +1 -1
  107. package/lib/forms/gate.js +28 -2
  108. package/lib/forms/gate.js.map +1 -1
  109. package/lib/forms/schema-errors.js +47 -0
  110. package/lib/forms/schema-errors.js.map +1 -0
  111. package/lib/forms/schema.js +29 -20
  112. package/lib/forms/schema.js.map +1 -1
  113. package/lib/index.js +148 -9
  114. package/lib/index.js.map +1 -1
  115. package/lib/integration/http.js +9 -10
  116. package/lib/integration/http.js.map +1 -1
  117. package/lib/integration/service.js +9 -0
  118. package/lib/integration/service.js.map +1 -1
  119. package/lib/personal-skills/provider.js +9 -1
  120. package/lib/personal-skills/provider.js.map +1 -1
  121. package/lib/personal-skills/service.js +4 -0
  122. package/lib/personal-skills/service.js.map +1 -1
  123. package/lib/personal-skills/shared-seed.js +65 -0
  124. package/lib/personal-skills/shared-seed.js.map +1 -0
  125. package/lib/prompt-notes.js +19 -1
  126. package/lib/prompt-notes.js.map +1 -1
  127. package/lib/provenance/collector.js +1 -0
  128. package/lib/provenance/collector.js.map +1 -1
  129. package/lib/provenance/extractors.js +6 -1
  130. package/lib/provenance/extractors.js.map +1 -1
  131. package/lib/qa-tools/catalog.js +18 -1
  132. package/lib/qa-tools/catalog.js.map +1 -1
  133. package/lib/qa-tools/code-files.js +207 -0
  134. package/lib/qa-tools/code-files.js.map +1 -0
  135. package/lib/qa-tools/code-tools.js +158 -0
  136. package/lib/qa-tools/code-tools.js.map +1 -0
  137. package/lib/qa-tools/docs-tools.js +49 -12
  138. package/lib/qa-tools/docs-tools.js.map +1 -1
  139. package/lib/qa-tools/index.js +4 -0
  140. package/lib/qa-tools/index.js.map +1 -1
  141. package/lib/qa-tools/request-user-form.js +26 -6
  142. package/lib/qa-tools/request-user-form.js.map +1 -1
  143. package/lib/request-scheduler.js +32 -5
  144. package/lib/request-scheduler.js.map +1 -1
  145. package/lib/resolve-config.js +9 -1
  146. package/lib/resolve-config.js.map +1 -1
  147. package/lib/secure-session.js +16 -4
  148. package/lib/secure-session.js.map +1 -1
  149. package/lib/session-model.js +43 -1
  150. package/lib/session-model.js.map +1 -1
  151. package/lib/shared/history-folder.js +18 -0
  152. package/lib/shared/history-folder.js.map +1 -0
  153. package/lib/typert.host.js +749 -162
  154. package/lib/typert.remote-client.d.ts +19 -7
  155. package/lib/typert.remote-client.js +684 -153
  156. package/lib/types/access/model-choices.d.ts +16 -0
  157. package/lib/types/access/model-policy.d.ts +13 -7
  158. package/lib/types/access/model.d.ts +9 -0
  159. package/lib/types/access/queue-policy.d.ts +30 -0
  160. package/lib/types/access/service.d.ts +27 -0
  161. package/lib/types/account-remotes.d.ts +5 -4
  162. package/lib/types/accounts/database.d.ts +5 -0
  163. package/lib/types/accounts/store.d.ts +6 -0
  164. package/lib/types/chat-sharing/transport.d.ts +1 -1
  165. package/lib/types/client/QaAccountsController.d.ts +3 -1
  166. package/lib/types/client/QaSessionController.d.ts +19 -5
  167. package/lib/types/client/QaSurface.d.ts +2 -0
  168. package/lib/types/client/admin/ModelPolicyEditor.d.ts +2 -0
  169. package/lib/types/client/code-source-styles.d.ts +2 -0
  170. package/lib/types/client/components/QaChatActions.d.ts +3 -1
  171. package/lib/types/client/components/QaCodeSourcePicker.d.ts +9 -0
  172. package/lib/types/client/components/QaComposer.d.ts +2 -0
  173. package/lib/types/client/components/QaComposerSettings.d.ts +16 -0
  174. package/lib/types/client/components/QaFormItem.d.ts +4 -2
  175. package/lib/types/client/components/QaHistoryCatalog.d.ts +19 -0
  176. package/lib/types/client/components/QaInputForm.d.ts +3 -2
  177. package/lib/types/client/components/QaModelSelector.d.ts +4 -0
  178. package/lib/types/client/components/QaSidebar.d.ts +3 -0
  179. package/lib/types/client/composer-settings-styles.d.ts +2 -0
  180. package/lib/types/client/forms-styles.d.ts +1 -1
  181. package/lib/types/client/history-organization-styles.d.ts +3 -0
  182. package/lib/types/client/history-organization.d.ts +38 -0
  183. package/lib/types/client/types.d.ts +12 -4
  184. package/lib/types/client/use-chat-code-source.d.ts +20 -0
  185. package/lib/types/client/use-history-organization.d.ts +8 -0
  186. package/lib/types/client/use-model-selection.d.ts +24 -0
  187. package/lib/types/client/use-qa-file-drop.d.ts +12 -0
  188. package/lib/types/code-source-selection.d.ts +28 -0
  189. package/lib/types/code-sources.js +2 -0
  190. package/lib/types/code-sources.js.map +1 -0
  191. package/lib/types/config-resolvers/integration.d.ts +7 -1
  192. package/lib/types/forms/errors.d.ts +3 -2
  193. package/lib/types/forms/gate.d.ts +4 -2
  194. package/lib/types/forms/schema-errors.d.ts +7 -0
  195. package/lib/types/forms/types.d.ts +14 -0
  196. package/lib/types/index.d.ts +38 -5
  197. package/lib/types/integration/http.d.ts +1 -3
  198. package/lib/types/integration/service.d.ts +2 -0
  199. package/lib/types/personal-skills/shared-seed.d.ts +12 -0
  200. package/lib/types/prompt-notes.d.ts +5 -1
  201. package/lib/types/qa-tools/catalog.d.ts +3 -2
  202. package/lib/types/qa-tools/code-files.d.ts +59 -0
  203. package/lib/types/qa-tools/code-tools.d.ts +7 -0
  204. package/lib/types/qa-tools/docs-tools.d.ts +4 -0
  205. package/lib/types/qa-tools/index.d.ts +2 -0
  206. package/lib/types/request-scheduler.d.ts +21 -2
  207. package/lib/types/session-model.d.ts +11 -2
  208. package/lib/types/shared/history-folder.d.ts +4 -0
  209. package/lib/types/types/accounts.d.ts +11 -0
  210. package/lib/types/types/capability.d.ts +31 -4
  211. package/lib/types/types/code-sources.d.ts +12 -0
  212. package/lib/types/types/config.d.ts +20 -0
  213. package/lib/types/types.d.ts +1 -0
  214. package/lib/user-workspace.js +7 -4
  215. package/lib/user-workspace.js.map +1 -1
  216. package/package.json +6 -5
@@ -0,0 +1,16 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { QaModelOptions } from "../types.js";
3
+ import type { SessionSelectModelRequest, SessionSelectModelValue } from "@deepseek-ai/dsh-api-session-controller/types";
4
+ import type { QaAccessService } from "./service.js";
5
+ import { type QaSessionModelPolicy } from "./model-policy.js";
6
+ /** Only explicit policy grants are exposed to an ordinary account. */
7
+ export declare function userModelOptions(ctx: Context, access: QaAccessService, token: string, sessionId: string | null, subroleId: string | null, preview: boolean): Promise<QaModelOptions>;
8
+ /** Validate both policy membership and live availability before a user mutation. */
9
+ export declare function assertUserModelSelection(ctx: Context, policy: () => QaSessionModelPolicy, request: {
10
+ readonly provider?: string;
11
+ readonly model?: string;
12
+ readonly reasoningEffort?: string | null;
13
+ }): Promise<void>;
14
+ /** Native durable per-session selection, without the operator-wide default save. */
15
+ export declare function selectUserModel(ctx: Context, access: QaAccessService, token: string, request: SessionSelectModelRequest): Promise<SessionSelectModelValue>;
16
+ //# sourceMappingURL=model-choices.d.ts.map
@@ -1,20 +1,23 @@
1
- import type { QaModelCatalogEntry, QaModelPair, ResolvedQaSurfaceConfig } from "../types.js";
1
+ import type { QaModelCatalogEntry, QaModelPair, QaModelReplacement, ResolvedQaSurfaceConfig } from "../types.js";
2
2
  /** The layer a resolved pair came from; the journal names it with the pair. */
3
3
  export type QaModelPolicyLayer = "account" | "subrole" | "deployment";
4
4
  /**
5
5
  * The model policy of one session: the pair its chats open on and the layer
6
6
  * that fixed it.
7
7
  *
8
- * That pair is what the fixed-model lockdown holds the session to. It is no
9
- * longer the deployment's pair, and it is not the model the visitor picked: a
10
- * role with a pair of its own is served by that pair, while a chat left on a
11
- * layer some policy has since replaced is outside its policy — exactly as a chat
12
- * left off the single global pair used to be.
8
+ * The fixed-model lockdown permits that default and explicitly granted
9
+ * alternatives. A user choice never changes the default for new chats.
13
10
  */
14
11
  export interface QaSessionModelPolicy {
15
12
  readonly pair: QaModelPair | undefined;
16
13
  readonly layer: QaModelPolicyLayer | undefined;
14
+ readonly redirectedFrom?: QaModelPair;
15
+ readonly allowedModels?: readonly QaModelPair[];
17
16
  }
17
+ /** Preserve an explicit empty override and reject malformed or excessive grants. */
18
+ export declare function normalizeAllowedModels(value: readonly QaModelPair[] | undefined, label: string): readonly QaModelPair[] | undefined;
19
+ /** Add explicit grants without changing which model a new chat starts on. */
20
+ export declare function withAllowedModels(policy: QaSessionModelPolicy, models: readonly QaModelPair[] | undefined, replacements: readonly QaModelReplacement[]): QaSessionModelPolicy;
18
21
  /** The session fields of the resolved config, as the policy reads them. */
19
22
  type SessionSlice = ResolvedQaSurfaceConfig["session"];
20
23
  /**
@@ -38,7 +41,10 @@ export declare function deploymentModelPair(session: Pick<SessionSlice, "provide
38
41
  * @param layers - the layers in priority order, `undefined` where a layer keeps
39
42
  * no opinion.
40
43
  */
41
- export declare function resolveModelPolicy(layers: Partial<Record<QaModelPolicyLayer, QaModelPair | undefined>>): QaSessionModelPolicy;
44
+ export declare function resolveModelPolicy(layers: Partial<Record<QaModelPolicyLayer, QaModelPair | undefined>>, replacements?: readonly QaModelReplacement[]): QaSessionModelPolicy;
45
+ /** Resolve only explicitly configured exact redirects; never guess from catalog order. */
46
+ export declare function replaceModelPair(pair: QaModelPair | undefined, replacements: readonly QaModelReplacement[]): QaModelPair | undefined;
47
+ export declare function normalizeModelReplacements(input: readonly QaModelReplacement[] | undefined): readonly QaModelReplacement[];
42
48
  /**
43
49
  * The provider, model and reasoning effort a live agent carries, as the
44
50
  * lockdown reads them off `agent.options`.
@@ -1,4 +1,13 @@
1
1
  import type { QaCapabilityConfig, QaCapabilityDescriptor, QaCapabilitySelection, QaEffectiveCapabilityPolicy, QaSkillAccess, QaSkillAssignmentOverride, QaSkillDescriptor, QaSubrole, QaToolSelection, QaUserAccess } from "../types.js";
2
+ /**
3
+ * Normalize one queue-slots override.
4
+ *
5
+ * An absent field keeps no opinion and the layer below it speaks. 0 is a real
6
+ * value — it lifts the ceiling for this role or account — while anything that
7
+ * is not an integer from 0 to {@link QA_MAX_ACTIVE_REQUESTS_MAX} is refused,
8
+ * the same range the deployment's own field accepts.
9
+ */
10
+ export declare function normalizeQueueSlots(value: number | undefined, label: string): number | undefined;
2
11
  /**
3
12
  * Normalize one Tool selection, accepting the pre-split flat list.
4
13
  *
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The request-ceiling chain, folded where a turn is admitted.
3
+ *
4
+ * The deployment's `session.maxActiveRequests` is the profile's default, and a
5
+ * role or an account may override it — the same three layers the model policy
6
+ * folds, read per turn rather than once at boot, so a settings write reaches
7
+ * the next admission without reloading the plugin.
8
+ */
9
+ /** The layer an effective ceiling came from; diagnostics name it with the value. */
10
+ export type QaQueueSlotsLayer = "account" | "subrole" | "deployment";
11
+ export interface QaQueueSlotsPolicy {
12
+ readonly limit: number;
13
+ readonly layer: QaQueueSlotsLayer;
14
+ }
15
+ /**
16
+ * Fold the named layers into one effective ceiling.
17
+ *
18
+ * The first layer that names a value wins outright: an account that overrides
19
+ * its role says how many slots it means, and a role that keeps no opinion
20
+ * defers to the deployment below it. 0 is a real value — a layer may drop the
21
+ * ceiling for its own principal even when the deployment sets one.
22
+ * @param layers - the layers in priority order; only `deployment` is required,
23
+ * because the profile default always speaks.
24
+ */
25
+ export declare function resolveQueueSlots(layers: {
26
+ readonly account?: number | undefined;
27
+ readonly subrole?: number | undefined;
28
+ readonly deployment: number;
29
+ }): QaQueueSlotsPolicy;
30
+ //# sourceMappingURL=queue-policy.d.ts.map
@@ -223,6 +223,32 @@ export declare class QaAccessService {
223
223
  * @param sessionId - the chat being opened.
224
224
  */
225
225
  modelPolicyFor(sessionId: string): QaSessionModelPolicy;
226
+ /** Authorize a draft role before exposing its model grants. */
227
+ draftModelPolicy(token: string, requested: string | null, preview: boolean): QaSessionModelPolicy;
228
+ /** Model mutations and their choices belong to the chat owner, including for admins. */
229
+ userSessionModelPolicy(token: string, sessionId: string): QaSessionModelPolicy;
230
+ private modelPolicyForAssignment;
231
+ /**
232
+ * The request ceiling one chat's turns fill against: the account's override,
233
+ * else the role the chat runs under, else the deployment's — the same layers
234
+ * {@link modelPolicyFor} folds, read per admission rather than once at boot.
235
+ * @param sessionId - the chat whose turn is being admitted or reported.
236
+ */
237
+ queueSlotsFor(sessionId: string): number;
238
+ /**
239
+ * The request ceiling one account's not-yet-created chats ask against: the
240
+ * pre-send read has a token but no chat to name, so the role layer is the
241
+ * account's default role.
242
+ * @param userId - the account the read is for; without one the deployment
243
+ * default speaks.
244
+ */
245
+ queueSlotsForUser(userId: string): number;
246
+ /**
247
+ * The named layers behind one chat's ceiling, for the resolver.
248
+ * @param sessionId - the chat, or nothing for the account-only read.
249
+ * @param userId - the account whose own chain answers when no chat exists.
250
+ */
251
+ private queueLayersFor;
226
252
  /**
227
253
  * Refuse a policy the Host cannot serve before it is stored.
228
254
  * @param pair - the pair as edited, before normalization.
@@ -242,6 +268,7 @@ export declare class QaAccessService {
242
268
  deleteSubrole(token: string, id: string, replacementId: string | null): void;
243
269
  updateCommon(token: string, input: QaCapabilitySelection): QaCapabilitySelection;
244
270
  updateAssignment(token: string, userId: string, input: QaUserAccess): Promise<QaUserAccess>;
271
+ assertAlternativesServable(input: readonly QaModelPair[] | undefined): Promise<void>;
245
272
  /** The accounts store, or the refusal this operation names. */
246
273
  private requireAccounts;
247
274
  /**
@@ -1,6 +1,6 @@
1
1
  import type { PluginLogger } from "@yadsh/dsh-plugin-log";
2
2
  import { QaAccounts } from "./accounts/store.js";
3
- import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, QaWhoamiResult, ResolvedQaSurfaceConfig } from "./types.js";
3
+ import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaHistoryFolderTarget, QaOwnedSessionsResult, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, QaWhoamiResult, ResolvedQaSurfaceConfig } from "./types.js";
4
4
  /**
5
5
  * Host-side bodies of the account remotes the `QaSurface` service exposes.
6
6
  * The `@Remote`-decorated signatures stay on the service class (typert code
@@ -42,9 +42,7 @@ export interface QaAccountRemotes {
42
42
  * delegated session, which takes session lineage, and that lives in
43
43
  * `QaAccessService.claimSessions` — one entry point, no path around it.
44
44
  */
45
- ownedSessions(token: string): {
46
- readonly ids: readonly string[];
47
- };
45
+ ownedSessions(token: string): QaOwnedSessionsResult;
48
46
  /**
49
47
  * Every chat-ownership entry with owner display names; the cross-user view
50
48
  * admin browsers group the sidebar by. Ordinary accounts are refused with
@@ -84,6 +82,9 @@ export interface QaAccountRemotes {
84
82
  setServiceTokenClarifications(token: string, tokenId: string, allowed: boolean): {
85
83
  readonly updated: boolean;
86
84
  };
85
+ setServiceTokenHistoryFolder(token: string, tokenId: string, folder: QaHistoryFolderTarget | null): {
86
+ readonly updated: boolean;
87
+ };
87
88
  }
88
89
  /** Build the account-remotes context for one `QaSurface` service instance. */
89
90
  export declare function createQaAccountRemotes(options: {
@@ -1,3 +1,4 @@
1
+ import type { QaHistoryFolderTarget } from "../types/accounts.js";
1
2
  import { SqliteDatabase } from "@yadsh/dsh-plugin-kit/sqlite";
2
3
  import type { QaSkillActivationRecord } from "../types.js";
3
4
  import { type AccountsFile, type StoredOwnership, type StoredUser } from "./file.js";
@@ -13,6 +14,7 @@ interface PasswordResetRow {
13
14
  * nowhere else.
14
15
  */
15
16
  export interface StoredServiceToken {
17
+ readonly historyFolder?: QaHistoryFolderTarget;
16
18
  readonly allowClarifications: boolean;
17
19
  readonly id: string;
18
20
  readonly userId: string;
@@ -108,6 +110,9 @@ export declare class QaAccountsDatabase {
108
110
  recordIntegrationRequestPolicy(sessionId: string, requestId: string, allowed: boolean): void;
109
111
  integrationClarificationsAllowed(sessionId: string, requestId: string): boolean;
110
112
  setServiceTokenClarifications(userId: string, tokenId: string, allowed: boolean): boolean;
113
+ setServiceTokenHistoryFolder(userId: string, tokenId: string, folder: QaHistoryFolderTarget | null): boolean;
114
+ recordIntegrationSessionFolder(userId: string, sessionId: string, folder: QaHistoryFolderTarget): void;
115
+ integrationSessionFolders(userId: string): Readonly<Record<string, QaHistoryFolderTarget>>;
111
116
  /**
112
117
  * One integration token by id, revoked and expired ones included.
113
118
  *
@@ -1,4 +1,5 @@
1
1
  import type { QaAccountIdentityField, QaAccountNotificationsInput, QaAccountProfile, QaAccountProfileInput, QaAccountRole, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaAccessUser, QaAdminAccountRow, QaClaimResult, QaEffectiveCapabilityPolicy, QaOwnershipEntry, QaIssuedServiceToken, QaPasswordResetRequest, QaServiceTokenSummary, QaSkillActivationRecord, QaUserAccess, QaWhoamiResult } from "../types.js";
2
+ import type { QaHistoryFolderTarget, QaOwnedSessionsResult } from "../types/accounts.js";
2
3
  import { type StoredOwnership, type StoredUser } from "./file.js";
3
4
  import { type QaServiceTokenScope } from "./service-token.js";
4
5
  export { QaAccountsError } from "./errors.js";
@@ -41,6 +42,7 @@ export interface QaSessionFacts {
41
42
  }
42
43
  /** What a caller asks for when it mints an integration token. */
43
44
  export interface QaServiceTokenIssueInput {
45
+ readonly historyFolder?: QaHistoryFolderTarget | null;
44
46
  /** A note for the operator's token list; empty becomes a generic label. */
45
47
  readonly label?: string;
46
48
  /** Requested scopes; unknown ones are dropped, explicit empty grants none. */
@@ -57,6 +59,7 @@ export interface QaServiceTokenIssueInput {
57
59
  }
58
60
  /** What a presented integration token resolved to. */
59
61
  export interface QaVerifiedServiceToken {
62
+ readonly historyFolder?: QaHistoryFolderTarget;
60
63
  readonly allowClarifications: boolean;
61
64
  readonly tokenId: string;
62
65
  readonly userId: string;
@@ -443,6 +446,9 @@ export declare class QaAccounts {
443
446
  recordIntegrationRequestPolicy(sessionId: string, requestId: string, allowed: boolean): void;
444
447
  setServiceTokenClarifications(token: string, tokenId: string, allowed: boolean): boolean;
445
448
  integrationClarificationsAllowed(sessionId: string, requestId: string): boolean;
449
+ setServiceTokenHistoryFolder(token: string, tokenId: string, input: QaHistoryFolderTarget | null): boolean;
450
+ recordIntegrationSessionFolder(userId: string, sessionId: string, folder: QaHistoryFolderTarget): void;
451
+ ownedSessionsWithFolders(token: string): QaOwnedSessionsResult;
446
452
  /** Revoke one integration token of an account the caller has established. */
447
453
  revokeServiceTokenFor(user: QaAccountUserPublic, tokenId: string): boolean;
448
454
  /** The caller if it administers the deployment, or a refusal. */
@@ -20,7 +20,7 @@ export declare class QaChatTransport {
20
20
  prompt(token: string, request: SessionPromptRequest, signal: AbortSignal): Promise<SessionPromptValue>;
21
21
  attachment(token: string, request: SessionAttachmentRequest): Promise<SessionAttachmentValue>;
22
22
  rename(token: string, request: SessionRenameRequest): Promise<SessionRenameValue>;
23
- selectModel(token: string, request: SessionSelectModelRequest): Promise<SessionSelectModelValue>;
23
+ selectModel(token: string, request: SessionSelectModelRequest, select?: () => Promise<SessionSelectModelValue>): Promise<SessionSelectModelValue>;
24
24
  cancel(token: string, id: string): Promise<SessionCancelValue>;
25
25
  updateQueue(token: string, request: SessionUpdateQueueRequest): Promise<SessionUpdateQueueValue>;
26
26
  private ownerOperation;
@@ -1,4 +1,4 @@
1
- import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, ResolvedQaSurfaceConfig } from "../types.js";
1
+ import type { QaAccountNotificationsInput, QaAccountProfileInput, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaHistoryFolderTarget, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, ResolvedQaSurfaceConfig } from "../types.js";
2
2
  import type { QaOidcBrowser } from "./oidc-browser.js";
3
3
  import type { QaAccountsApi, QaIntegrationTokenResult, StorageLike } from "./types.js";
4
4
  /**
@@ -31,6 +31,7 @@ export type QaAccountsSnapshot = {
31
31
  * by this list rather than by the sidebar's.
32
32
  */
33
33
  readonly ownIds: readonly string[];
34
+ readonly integrationFolders?: Readonly<Record<string, QaHistoryFolderTarget>>;
34
35
  /**
35
36
  * The full ownership map; admins only, empty for ordinary accounts and
36
37
  * whenever the admin listing was refused or unavailable.
@@ -186,6 +187,7 @@ export declare class QaAccountsController {
186
187
  */
187
188
  revokeServiceToken(tokenId: string): Promise<QaIntegrationTokenResult<null>>;
188
189
  setServiceTokenClarifications(tokenId: string, allowed: boolean): Promise<QaIntegrationTokenResult<null>>;
190
+ setServiceTokenHistoryFolder(tokenId: string, folder: QaHistoryFolderTarget | null): Promise<QaIntegrationTokenResult<null>>;
189
191
  private authenticate;
190
192
  /** Own the account, migrate the legacy index, replay held ratings, then auth. */
191
193
  private enterSession;
@@ -1,8 +1,9 @@
1
1
  import type { ConnectionGenerationState } from "@deepseek-ai/dsh-client-connection/client";
2
2
  import type { QaSharingApi } from "./chat-sharing-api.js";
3
3
  import type { QaAttachmentDraft, QaQueueOperation, QaSessionState, ResolvedQaSurfaceConfig } from "../types.js";
4
+ import { type QaFormApi } from "./forms.js";
4
5
  import type { QaApprovalApi, QaConversation, QaCreateSession, QaForkSession, QaFileUpload, QaQueueStatusRemote, QaSecureSession, QaSessions, QaSessionsApi, QaQuestionApi, QaSlashApi, QaSourceApi, StorageLike } from "./types.js";
5
- import type { QaApprovalDecision, QaQuestionAnswerItem } from "../types.js";
6
+ import type { QaApprovalDecision, QaQuestionAnswerItem, QaModelPair } from "../types.js";
6
7
  declare module "@deepseek-ai/dsh-api-session-controller/client" {
7
8
  interface SessionReferenceSourceMap {
8
9
  /** The QA surface holds the Session of the chat it is showing. */
@@ -25,6 +26,8 @@ export interface QaAccountsFacade {
25
26
  readonly onAuthRequired: () => void;
26
27
  }
27
28
  export interface QaSessionControllerOptions {
29
+ /** Persist a draft's source choice before it can submit its first prompt. */
30
+ readonly prepareSession?: (sessionId: string) => Promise<void>;
28
31
  readonly sharingApi?: QaSharingApi;
29
32
  readonly sessions: QaSessions;
30
33
  readonly api: QaSessionsApi;
@@ -47,6 +50,8 @@ export interface QaSessionControllerOptions {
47
50
  * does not answer questions: the surface then only renders what it can.
48
51
  */
49
52
  readonly questionApi?: QaQuestionApi;
53
+ /** Confirm this owner's form renderer before a prompt can request input. */
54
+ readonly formApi?: Pick<QaFormApi, "list">;
50
55
  /**
51
56
  * The slash half of the Host namespace. Absent on a page whose Host build
52
57
  * predates it: the palette then never appears and ordinary prompts keep
@@ -89,7 +94,9 @@ export interface QaSessionControllerOptions {
89
94
  export declare class QaSessionController {
90
95
  private readonly listeners;
91
96
  private readonly sessions;
97
+ private readonly modelApi;
92
98
  private readonly createSessionRemote;
99
+ private readonly prepareSession?;
93
100
  private readonly forkSessionRemote?;
94
101
  private forking;
95
102
  private inheritedThroughSeq;
@@ -97,6 +104,7 @@ export declare class QaSessionController {
97
104
  private readonly connection;
98
105
  private readonly config;
99
106
  private readonly secureSessionRemote;
107
+ private readonly formApi;
100
108
  private readonly sourceApi;
101
109
  private readonly chats;
102
110
  private readonly accounts;
@@ -231,6 +239,8 @@ export declare class QaSessionController {
231
239
  * favor of a fresh Host-owned session.
232
240
  */
233
241
  selectSubrole(subroleId: string, adminPreview?: boolean): Promise<void>;
242
+ /** Materialize a draft on demand and change only that chat's model. */
243
+ selectModel(pair: QaModelPair): Promise<string>;
234
244
  /**
235
245
  * The attested live session a submission may ride, or `undefined` when the
236
246
  * caller must abort. Materializing a draft chat, attesting policy and
@@ -324,10 +334,14 @@ export declare class QaSessionController {
324
334
  * lazily ({@link materializeDraft}). Fixed-policy deployments cannot draft.
325
335
  *
326
336
  * A draft is a chat of its own, so entering one takes the screen the way
327
- * {@link switchTo} does; see the note where the generation moves. Stopping the
328
- * turn that was running first takes a round-trip, and a chat opened through it
329
- * keeps the screen: the draft is what the visitor asked for before that click,
330
- * so this call gives up rather than taking the chat back from under them.
337
+ * {@link switchTo} does; see the note where the generation moves. The turn
338
+ * another chat is answering keeps answering: the deployment's slots and its
339
+ * FIFO queue exist so a visitor can open a fresh chat — or send into one —
340
+ * without stopping the turn that is still working. This method used to stop
341
+ * it first, which is how «Новый чат» came to abort the running turn of the
342
+ * chat it left (every second send of the 28.10 acceptance wave died exactly
343
+ * there); a stopped turn is now the stop button's own gesture and nothing
344
+ * else.
331
345
  */
332
346
  startDraft(policyChange?: boolean): Promise<void>;
333
347
  /**
@@ -5,6 +5,7 @@ import type { QaRouteController } from "./QaRouteController.js";
5
5
  import type { QaAccountsController } from "./QaAccountsController.js";
6
6
  import type { QaApprovalApi, QaAccessApi, QaAdminApi, QaConversation, QaCreateSession, QaForkSession, QaFileUpload, QaQueueStatusRemote, QaSecureSession, QaSessions, QaSessionsApi, QaQuestionApi, QaSkillApi, QaSlashApi, QaSourceApi } from "./types.js";
7
7
  import type { QaFormApi } from "./forms.js";
8
+ import type { QaChatCodeSourceApi } from "./types.js";
8
9
  import type { QaSurfacePanelRegistry } from "./panels/registry.js";
9
10
  import type { QaUserSettingsSections } from "./settings-extensions/index.js";
10
11
  import type { QaAuditController } from "./audit/controller.js";
@@ -29,6 +30,7 @@ export interface QaSurfaceFace {
29
30
  */
30
31
  readonly adminApi?: QaAdminApi;
31
32
  readonly sourceApi: QaSourceApi;
33
+ readonly codeSourceApi?: QaChatCodeSourceApi;
32
34
  /**
33
35
  * Personal-skill half of the plugin's namespace. Absent on a page whose Host
34
36
  * build does not answer it; the settings dialog then has no skills section.
@@ -19,5 +19,7 @@ export declare function ModelPolicyEditor(props: {
19
19
  /** Prefix of the test ids, so one component serves both editors. */
20
20
  readonly testId: string;
21
21
  readonly onChange: (value: QaModelPair | undefined) => void;
22
+ readonly allowedModels?: readonly QaModelPair[];
23
+ readonly onAllowedModelsChange?: (value: readonly QaModelPair[] | undefined) => void;
22
24
  }): import("react/jsx-runtime").JSX.Element;
23
25
  //# sourceMappingURL=ModelPolicyEditor.d.ts.map
@@ -0,0 +1,2 @@
1
+ export declare const codeSourceStyles: string;
2
+ //# sourceMappingURL=code-source-styles.d.ts.map
@@ -1,7 +1,9 @@
1
+ import type { QaHistoryRowActions } from "./QaHistoryCatalog.js";
1
2
  /** Quiet actions attached to one owned chat, outside the conversation header. */
2
3
  export declare function QaChatActions(props: {
3
4
  name: string;
4
- onShare: () => void;
5
+ onShare?: () => void;
5
6
  onDelete?: (trigger: HTMLButtonElement) => void;
7
+ organization?: QaHistoryRowActions;
6
8
  }): import("react/jsx-runtime").JSX.Element;
7
9
  //# sourceMappingURL=QaChatActions.d.ts.map
@@ -0,0 +1,9 @@
1
+ import type { QaChatCodeSources } from "../../types/code-sources.js";
2
+ export declare function QaCodeSourcePicker(props: {
3
+ readonly value: QaChatCodeSources;
4
+ readonly pending: boolean;
5
+ readonly error: string | null;
6
+ readonly disabled: boolean;
7
+ readonly onSelect: (source: string | null) => Promise<void>;
8
+ }): import("react/jsx-runtime").JSX.Element | null;
9
+ //# sourceMappingURL=QaCodeSourcePicker.d.ts.map
@@ -1,7 +1,9 @@
1
+ import type { ReactNode } from "react";
1
2
  import type { QaAttachmentDraft, QaSlashView, ResolvedQaSlashCommands } from "../../types.js";
2
3
  import { type QaAttachmentLimits } from "../attachments.js";
3
4
  import type { QaQuickQuestion } from "../types.js";
4
5
  export interface QaComposerProps {
6
+ readonly modelSelector?: ReactNode;
5
7
  readonly fastMode?: boolean;
6
8
  readonly onFastModeChange?: (enabled: boolean) => void;
7
9
  readonly placeholder: string;
@@ -0,0 +1,16 @@
1
+ import type { QaChatCodeSources } from "../../types/code-sources.js";
2
+ import { type QaModelSelectionProps } from "../use-model-selection.js";
3
+ export interface QaComposerSettingsProps {
4
+ readonly model?: QaModelSelectionProps | undefined;
5
+ readonly code?: {
6
+ readonly value: QaChatCodeSources;
7
+ readonly pending: boolean;
8
+ readonly error: string | null;
9
+ readonly disabled: boolean;
10
+ readonly defaultLabel?: string | undefined;
11
+ readonly onSelect: (source: string | null) => Promise<boolean>;
12
+ } | undefined;
13
+ }
14
+ /** A single quiet trigger; model and source choices keep their own Host boundaries. */
15
+ export declare function QaComposerSettings(props: QaComposerSettingsProps): import("react/jsx-runtime").JSX.Element | null;
16
+ //# sourceMappingURL=QaComposerSettings.d.ts.map
@@ -1,9 +1,10 @@
1
- import type { QaFormAnswer, QaFormItem as Item, QaFormAttachment, QaFormUpload } from "../../forms/types.js";
1
+ import type { QaFormAnswer, QaFormItem as Item, QaFormAttachment, QaFormUpload, QaFormFieldError } from "../../forms/types.js";
2
2
  interface Props {
3
3
  item: Item;
4
4
  answer: QaFormAnswer | null;
5
5
  files: QaFormAttachment[];
6
6
  uploads: QaFormUpload[];
7
+ errors?: QaFormFieldError[];
7
8
  disabled: boolean;
8
9
  onAnswer(answer: QaFormAnswer | null): void;
9
10
  onUpload(file: File, target?: {
@@ -14,7 +15,8 @@ interface Props {
14
15
  onCancel(upload: QaFormUpload): void;
15
16
  onOpen(file: QaFormAttachment): void;
16
17
  }
17
- /** Input requirements are separate from file requirements, including in the labels. */
18
+ export declare function formFileSize(bytes: number): string;
19
+ /** Each item's answers, file requirements and validation stay in one visual group. */
18
20
  export declare function QaFormItem(props: Props): import("react/jsx-runtime").JSX.Element;
19
21
  export {};
20
22
  //# sourceMappingURL=QaFormItem.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from "react";
2
+ import type { QaChatRow, QaSidebarSection } from "./QaSidebar.js";
3
+ export interface QaHistoryRowActions {
4
+ readonly pinned: boolean;
5
+ readonly onTogglePin: () => void;
6
+ readonly onMove: () => void;
7
+ readonly onEarlier?: () => void;
8
+ readonly onLater?: () => void;
9
+ }
10
+ /** Presentation only: no native workspace or session mutation is issued here. */
11
+ export declare function QaHistoryCatalog(props: {
12
+ readonly storageKey: string;
13
+ readonly rows: readonly QaChatRow[];
14
+ readonly query: string;
15
+ readonly integrationFolders?: Readonly<Record<string, import("../../types/accounts.js").QaHistoryFolderTarget>>;
16
+ readonly sectionsOf: (rows: readonly QaChatRow[]) => readonly QaSidebarSection[];
17
+ readonly renderRow: (row: QaChatRow, actions: QaHistoryRowActions) => ReactNode;
18
+ }): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=QaHistoryCatalog.d.ts.map
@@ -10,9 +10,10 @@ interface Props {
10
10
  onStop(): Promise<void>;
11
11
  onResume(id: string): void;
12
12
  onTakeover(active: boolean): void;
13
+ onDismiss?(): void;
13
14
  }
14
- export declare function QaInputForm(props: Props): import("react/jsx-runtime").JSX.Element;
15
+ export declare function QaInputForm(props: Props): import("react/jsx-runtime").JSX.Element | null;
15
16
  /** Session/identity-keyed parent discards account-local state and aborts transfers on switch. */
16
- export declare function QaInputForms(props: Omit<Props, "form">): import("react/jsx-runtime").JSX.Element | null;
17
+ export declare function QaInputForms(props: Omit<Props, "form">): import("react/jsx-runtime").JSX.Element;
17
18
  export {};
18
19
  //# sourceMappingURL=QaInputForm.d.ts.map
@@ -0,0 +1,4 @@
1
+ import { type QaModelSelectionProps } from "../use-model-selection.js";
2
+ /** The server filters choices and persists selection on the native session. */
3
+ export declare function QaModelSelector(props: QaModelSelectionProps): import("react/jsx-runtime").JSX.Element | null;
4
+ //# sourceMappingURL=QaModelSelector.d.ts.map
@@ -54,6 +54,9 @@ export interface QaSidebarProps {
54
54
  readonly logoUrl: string | null;
55
55
  /** Storage prefix used to remember the collapsed state across reloads. */
56
56
  readonly stateKey: string;
57
+ /** Deployment and account scoped key for virtual history metadata. */
58
+ readonly organizationKey?: string;
59
+ readonly integrationFolders?: Readonly<Record<string, import("../../types/accounts.js").QaHistoryFolderTarget>>;
57
60
  readonly showNewChat: boolean;
58
61
  readonly busy: boolean;
59
62
  readonly onSwitch: (sessionId: string) => void;
@@ -0,0 +1,2 @@
1
+ export declare const composerSettingsStyles: string;
2
+ //# sourceMappingURL=composer-settings-styles.d.ts.map
@@ -1,2 +1,2 @@
1
- export declare const QA_FORM_STYLES = "\n.dsh-qa-form{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;max-height:75vh;overflow:hidden;overflow-wrap:anywhere;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column}\n.dsh-qa-form__content{overflow:auto;min-height:0;padding:0 2px}.dsh-qa-form>header,.dsh-qa-form>footer,.dsh-qa-form>nav,.dsh-qa-form>p{flex-shrink:0}.dsh-qa-form>footer{border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px;margin-top:8px}\n.dsh-qa-form>header{max-height:96px;overflow:auto}\n.dsh-qa-form header,.dsh-qa-form footer,.dsh-qa-form nav{display:flex;gap:8px;align-items:center;flex-wrap:wrap}\n.dsh-qa-form header{justify-content:space-between}.dsh-qa-form nav{margin:12px 0}.dsh-qa-form h3{margin:12px 0}\n.dsh-qa-form button{font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px;cursor:pointer}\n.dsh-qa-form button:disabled{opacity:.55;cursor:default}.dsh-qa-form button:focus-visible,.dsh-qa-form input:focus-visible,.dsh-qa-form textarea:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}\n.dsh-qa-form__item{min-width:0;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);padding:12px 0;margin:0 0 12px}.dsh-qa-form legend{font-weight:600}.dsh-qa-form small{font-weight:400;display:block;color:var(--dsw-alias-label-tertiary)}\n.dsh-qa-form__hint{font-size:13px;color:var(--dsw-alias-label-secondary)}.dsh-qa-form label{display:block}.dsh-qa-form input[type=text],.dsh-qa-form textarea{display:block;box-sizing:border-box;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px;margin:6px 0}\n.dsh-qa-form__choices>label{display:flex;gap:8px;align-items:baseline;margin:8px 0}.dsh-qa-form__files{border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;padding:12px;margin-top:12px}.dsh-qa-form input[type=file]{max-width:100%;display:block;margin:8px 0}.dsh-qa-form__files ul{padding-left:18px}.dsh-qa-form__files li{margin:10px 0}.dsh-qa-form__files button{margin:4px}.dsh-qa-form__review>div{border-bottom:1px solid var(--dsw-alias-border-l2);margin-bottom:12px}.dsh-qa-form [role=alert]{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);padding:10px;border-radius:8px}\n@media(max-width:600px){.dsh-qa-form{padding:12px;max-height:80vh}.dsh-qa-form footer button{flex:1 1 auto}.dsh-qa-form nav button{font-size:12px}}\n";
1
+ export declare const QA_FORM_STYLES = "\n.dsh-qa-form{box-sizing:border-box;min-width:0;max-height:min(720px,calc(100dvh - 160px));overflow:hidden;overflow-wrap:anywhere;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;display:flex;flex-direction:column;font:inherit;font-size:14px;line-height:20px}\n.dsh-qa-form *{box-sizing:border-box}.dsh-qa-form p,.dsh-qa-form h3,.dsh-qa-form h4{margin:0}\n.dsh-qa-form .title-3{font-size:16px;line-height:24px;font-weight:600}.dsh-qa-form .title-4{font-size:14px;line-height:20px;font-weight:600}.dsh-qa-form .caption-5{font-size:12px;line-height:16px;font-weight:400}\n.dsh-qa-form>header{padding:16px 24px;display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-shrink:0}.dsh-qa-form__heading{min-width:0;display:grid;gap:4px}.dsh-qa-form__heading strong{display:block;text-wrap:pretty}\n.dsh-qa-form__description{color:var(--dsw-alias-label-secondary);text-wrap:pretty}.dsh-qa-form__hint{font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary)}\n.dsh-qa-form__badge{display:inline-flex;flex-shrink:0;align-items:center;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);padding:4px 8px;white-space:nowrap}\n.dsh-qa-form__status{display:flex;flex-wrap:wrap;gap:4px 16px;padding:0 24px 12px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px;flex-shrink:0}\n.dsh-qa-form>nav{display:flex;gap:8px;overflow:auto;flex-shrink:0;padding:0 24px 16px;scrollbar-width:thin}.dsh-qa-form>nav button{display:flex;align-items:center;gap:8px;white-space:nowrap;font-weight:500;background:transparent}\n.dsh-qa-form__step-number{width:24px;height:24px;border-radius:999px;display:inline-grid;place-items:center;background:var(--dsw-alias-bg-module-platform);font-size:12px;line-height:16px;flex-shrink:0}\n.dsh-qa-form>nav button[aria-current=step]{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 12%,transparent);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary)}\n.dsh-qa-form>nav button[aria-current=step] .dsh-qa-form__step-number{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 24%,transparent)}\n.dsh-qa-form__content{overflow:auto;min-height:0;padding:0 24px 16px;overscroll-behavior:contain;scrollbar-gutter:stable}.dsh-qa-form__step{display:grid;gap:16px}.dsh-qa-form__step-heading{display:grid;gap:4px}\n.dsh-qa-form__item{min-width:0;margin:0;padding:16px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3)}\n.dsh-qa-form__item-title{max-width:100%;padding:0 4px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;color:var(--dsw-alias-label-primary)}\n.dsh-qa-form__item>.dsh-qa-form__description,.dsh-qa-form__item>.dsh-qa-form__hint{margin-bottom:8px}.dsh-qa-form__answer{display:block}\n.dsh-qa-form input[type=text],.dsh-qa-form textarea{display:block;min-width:0;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px;min-height:40px}.dsh-qa-form textarea{resize:vertical;min-height:96px}\n.dsh-qa-form__choices{display:grid;gap:8px}.dsh-qa-form__choice{display:grid;grid-template-columns:16px minmax(0,1fr);align-items:start;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:12px;background:var(--dsw-alias-bg-layer-2);cursor:pointer}\n.dsh-qa-form__choice:hover{border-color:var(--dsw-alias-label-dimmed)}.dsh-qa-form__choice.is-selected{border-color:var(--dsw-alias-state-business-primary);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,var(--dsw-alias-bg-layer-2))}\n.dsh-qa-form__choice input{width:16px;height:16px;margin:2px 0 0;accent-color:var(--dsw-alias-state-business-primary)}.dsh-qa-form__choice-copy{display:grid;gap:4px;min-width:0}.dsh-qa-form__choice-copy>.title-4{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-qa-form__recommended{color:var(--dsw-alias-state-business-primary);font-weight:500}.dsh-qa-form__other{display:grid;gap:8px;padding-top:4px}\n.dsh-qa-form button,.dsh-qa-form__picker{appearance:none;font:inherit;font-weight:500;line-height:20px;color:inherit;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px;min-height:40px;cursor:pointer;text-align:center;display:inline-flex;align-items:center;justify-content:center;gap:8px}\n.dsh-qa-form button:hover,.dsh-qa-form__picker:hover{border-color:var(--dsw-alias-label-dimmed)}.dsh-qa-form button:disabled,.dsh-qa-form .is-disabled{opacity:.5;cursor:default}\n.dsh-qa-form button:focus-visible,.dsh-qa-form input:focus-visible,.dsh-qa-form textarea:focus-visible,.dsh-qa-form__picker:focus-within{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}\n.dsh-qa-form .dsh-qa-form__primary,.dsh-qa-form__dropzone>.dsh-qa-form__picker{font-weight:600;border-color:var(--dsw-alias-state-business-primary);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,var(--dsw-alias-bg-layer-2))}\n.dsh-qa-form .dsh-qa-form__text-button{background:transparent;justify-self:start}.dsh-qa-form__files{display:grid;gap:8px;margin-top:12px;padding:16px;border:1px dashed var(--dsw-alias-label-dimmed);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}\n.dsh-qa-form__file-heading{display:flex;align-items:center;justify-content:space-between;gap:8px}.dsh-qa-form__dropzone{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding-top:4px}.dsh-qa-form__picker{position:relative;max-width:100%}\n.dsh-qa-form__picker input[type=file]{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden;clip-path:inset(50%)}\n.dsh-qa-form__file-list{display:grid;gap:8px;padding:0;margin:4px 0 0;list-style:none}.dsh-qa-form__file-row,.dsh-qa-form__upload{display:flex;align-items:center;gap:12px;flex-wrap:wrap;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:12px;background:var(--dsw-alias-bg-layer-3)}\n.dsh-qa-form__file-copy{display:grid;gap:4px;min-width:0;flex:1 1 160px}.dsh-qa-form__file-actions{display:flex;gap:8px;flex-wrap:wrap}.dsh-qa-form__file-actions button,.dsh-qa-form__picker--small{font-size:12px;line-height:16px;min-height:32px;padding:4px 8px}\n.dsh-qa-form__field-errors{display:grid;gap:4px;margin-top:8px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:16px}.dsh-qa-form [aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary)}\n.dsh-qa-form [role=alert]{display:grid;gap:8px;color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-state-error-primary);padding:12px;border-radius:8px;margin-bottom:16px}\n.dsh-qa-form>footer{display:grid;gap:8px;padding:12px 24px 16px;flex-shrink:0;background:var(--dsw-alias-bg-layer-2)}.dsh-qa-form__actions{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.dsh-qa-form__navigation,.dsh-qa-form__secondary{display:flex;gap:8px;flex-wrap:wrap}\n.dsh-qa-form__review{display:grid;gap:16px}.dsh-qa-form__review-step{display:grid;gap:12px}.dsh-qa-form__review-item{display:grid;gap:8px;padding:16px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);white-space:pre-wrap}\n.dsh-qa-form--summary{padding:16px 24px;flex-direction:row;align-items:center;justify-content:space-between;gap:16px}.dsh-qa-form--summary>div:first-child{display:grid;gap:4px;min-width:0}\n@media(max-width:600px){.dsh-qa-form{max-height:calc(100dvh - 160px);border-radius:12px}.dsh-qa-form>header{padding:12px 16px;gap:8px}.dsh-qa-form__status{padding:0 16px 12px}.dsh-qa-form>nav{padding:0 16px 12px}.dsh-qa-form__content{padding:0 16px 12px}.dsh-qa-form__item,.dsh-qa-form__files{padding:12px}.dsh-qa-form>footer{padding:12px 16px}.dsh-qa-form__actions{flex-direction:column;align-items:stretch}.dsh-qa-form__navigation{display:grid;grid-template-columns:1fr 1fr}.dsh-qa-form__secondary{justify-content:space-between}.dsh-qa-form__secondary button{font-size:12px;line-height:16px;min-height:32px;padding:4px 8px}.dsh-qa-form--summary{padding:12px 16px;align-items:flex-start}.dsh-qa-form__file-row{align-items:flex-start}}\n";
2
2
  //# sourceMappingURL=forms-styles.d.ts.map
@@ -0,0 +1,3 @@
1
+ /** Virtual history controls inherit the same palette as the existing sidebar. */
2
+ export declare const historyOrganizationStyles: string;
3
+ //# sourceMappingURL=history-organization-styles.d.ts.map
@@ -0,0 +1,38 @@
1
+ import type { QaHistoryFolderTarget } from "../types/accounts.js";
2
+ /** Browser-only presentation metadata. No entry is a filesystem path. */
3
+ export interface QaHistoryFolder {
4
+ readonly id: string;
5
+ readonly name: string;
6
+ readonly collapsed: boolean;
7
+ }
8
+ export interface QaHistoryOrganization {
9
+ readonly folders: readonly QaHistoryFolder[];
10
+ readonly assignments: Readonly<Record<string, string>>;
11
+ readonly pinned: readonly string[];
12
+ readonly order: readonly string[];
13
+ /** Routing was imported, or the reader explicitly chose a folder/root. */
14
+ readonly folderHandled?: readonly string[];
15
+ }
16
+ export declare const EMPTY_HISTORY: QaHistoryOrganization;
17
+ /** Treat storage as untrusted and tolerate older, partial or damaged values. */
18
+ export declare function parseHistoryOrganization(raw: string | null): QaHistoryOrganization;
19
+ export declare function historyFolderOf(state: QaHistoryOrganization, id: string): string | null;
20
+ /** A removed folder releases its chats, including pinned chats, to the root. */
21
+ export declare function removeHistoryFolder(state: QaHistoryOrganization, folderId: string): QaHistoryOrganization;
22
+ /** Insert relative to the visible target without losing filtered or unloaded ids. */
23
+ export declare function insertHistoryId(ids: readonly string[], id: string, before?: string): string[];
24
+ export type QaHistoryDestination = {
25
+ readonly kind: "pinned";
26
+ } | {
27
+ readonly kind: "folder";
28
+ readonly id: string | null;
29
+ };
30
+ /** Moving a pinned chat into a folder unpins it; pinning retains its folder. */
31
+ export declare function moveHistoryChat(state: QaHistoryOrganization, id: string, destination: QaHistoryDestination, visibleOrder: readonly string[], before?: string): QaHistoryOrganization;
32
+ /** Apply server defaults once; subsequent manual moves and deletions win. */
33
+ export declare function importIntegrationFolders(state: QaHistoryOrganization, hints: Readonly<Record<string, QaHistoryFolderTarget>>, visibleIds: ReadonlySet<string>): QaHistoryOrganization;
34
+ /** Unordered rows keep recency order until the reader arranges them. */
35
+ export declare function orderHistoryRows<T extends {
36
+ readonly id: string;
37
+ }>(rows: readonly T[], order: readonly string[]): T[];
38
+ //# sourceMappingURL=history-organization.d.ts.map