@yadsh/dsh-qa-surface 0.10.0 → 0.11.1

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 (169) hide show
  1. package/README.md +310 -48
  2. package/lib/access/service.js +49 -10
  3. package/lib/access/service.js.map +1 -1
  4. package/lib/account-remotes.js +41 -0
  5. package/lib/account-remotes.js.map +1 -1
  6. package/lib/accounts/database.js +140 -0
  7. package/lib/accounts/database.js.map +1 -1
  8. package/lib/accounts/errors.js.map +1 -1
  9. package/lib/accounts/service-token.js +103 -0
  10. package/lib/accounts/service-token.js.map +1 -0
  11. package/lib/accounts/store.js +291 -5
  12. package/lib/accounts/store.js.map +1 -1
  13. package/lib/admin/conversation-log.js +14 -8
  14. package/lib/admin/conversation-log.js.map +1 -1
  15. package/lib/admin/permissions.js +5 -0
  16. package/lib/admin/permissions.js.map +1 -1
  17. package/lib/admin/quality-store.js +4 -0
  18. package/lib/admin/quality-store.js.map +1 -1
  19. package/lib/admin/service.js +173 -0
  20. package/lib/admin/service.js.map +1 -1
  21. package/lib/admin/session-log.js +39 -8
  22. package/lib/admin/session-log.js.map +1 -1
  23. package/lib/attachment-rules.js +39 -6
  24. package/lib/attachment-rules.js.map +1 -1
  25. package/lib/cli.js +83 -1
  26. package/lib/cli.js.map +1 -1
  27. package/lib/client/QaAccountsController.js +213 -4
  28. package/lib/client/QaAccountsController.js.map +1 -1
  29. package/lib/client/QaSessionController.js +37 -21
  30. package/lib/client/QaSessionController.js.map +1 -1
  31. package/lib/client/QaSurface.js +28 -4
  32. package/lib/client/QaSurface.js.map +1 -1
  33. package/lib/client/admin/QaAdmin.js +9 -1
  34. package/lib/client/admin/QaAdmin.js.map +1 -1
  35. package/lib/client/admin/pages/SkillFiles.js +78 -0
  36. package/lib/client/admin/pages/SkillFiles.js.map +1 -0
  37. package/lib/client/admin/pages/Users.js +65 -1
  38. package/lib/client/admin/pages/Users.js.map +1 -1
  39. package/lib/client/admin/routes.js +8 -1
  40. package/lib/client/admin/routes.js.map +1 -1
  41. package/lib/client/admin/styles.js +2 -0
  42. package/lib/client/admin/styles.js.map +1 -1
  43. package/lib/client/attachments.js +41 -11
  44. package/lib/client/attachments.js.map +1 -1
  45. package/lib/client/components/QaAuthGate.js +19 -10
  46. package/lib/client/components/QaAuthGate.js.map +1 -1
  47. package/lib/client/components/QaChangelog.js +43 -0
  48. package/lib/client/components/QaChangelog.js.map +1 -1
  49. package/lib/client/index.js +13 -0
  50. package/lib/client/index.js.map +1 -1
  51. package/lib/client/lineage.js +64 -0
  52. package/lib/client/lineage.js.map +1 -1
  53. package/lib/client/settings/sections/attachments.js +6 -5
  54. package/lib/client/settings/sections/attachments.js.map +1 -1
  55. package/lib/client/settings/sections/notes.js +22 -4
  56. package/lib/client/settings/sections/notes.js.map +1 -1
  57. package/lib/client/styles.js +9 -0
  58. package/lib/client/styles.js.map +1 -1
  59. package/lib/client/types.js.map +1 -1
  60. package/lib/client/user-settings/IntegrationTokensPage.js +150 -0
  61. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -0
  62. package/lib/client/user-settings/PasswordSettingsPage.js +48 -0
  63. package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -0
  64. package/lib/client/user-settings/SkillCatalog.js +2 -2
  65. package/lib/client/user-settings/SkillCatalog.js.map +1 -1
  66. package/lib/client/user-settings/UserSettingsDialog.js +17 -2
  67. package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
  68. package/lib/client/user-settings/copy.js +20 -0
  69. package/lib/client/user-settings/copy.js.map +1 -1
  70. package/lib/client.js +2480 -137
  71. package/lib/client.js.map +1 -1
  72. package/lib/config-resolvers/attachments.js +6 -5
  73. package/lib/config-resolvers/attachments.js.map +1 -1
  74. package/lib/config-resolvers/defaults.js +23 -2
  75. package/lib/config-resolvers/defaults.js.map +1 -1
  76. package/lib/config-resolvers/integration.js +94 -0
  77. package/lib/config-resolvers/integration.js.map +1 -0
  78. package/lib/config-resolvers/notes.js +2 -0
  79. package/lib/config-resolvers/notes.js.map +1 -1
  80. package/lib/config.js +72 -0
  81. package/lib/config.js.map +1 -1
  82. package/lib/index.js +197 -2
  83. package/lib/index.js.map +1 -1
  84. package/lib/integration/answer.js +100 -0
  85. package/lib/integration/answer.js.map +1 -0
  86. package/lib/integration/attachments.js +246 -0
  87. package/lib/integration/attachments.js.map +1 -0
  88. package/lib/integration/contract.js +53 -0
  89. package/lib/integration/contract.js.map +1 -0
  90. package/lib/integration/host-runner.js +243 -0
  91. package/lib/integration/host-runner.js.map +1 -0
  92. package/lib/integration/http.js +576 -0
  93. package/lib/integration/http.js.map +1 -0
  94. package/lib/integration/service.js +373 -0
  95. package/lib/integration/service.js.map +1 -0
  96. package/lib/integration/transcript-reader.js +202 -0
  97. package/lib/integration/transcript-reader.js.map +1 -0
  98. package/lib/integration/transcript.js +148 -0
  99. package/lib/integration/transcript.js.map +1 -0
  100. package/lib/personal-skills/index.js +2 -2
  101. package/lib/personal-skills/index.js.map +1 -1
  102. package/lib/personal-skills/paths.js +14 -8
  103. package/lib/personal-skills/paths.js.map +1 -1
  104. package/lib/personal-skills/provider.js +16 -4
  105. package/lib/personal-skills/provider.js.map +1 -1
  106. package/lib/personal-skills/service.js +240 -30
  107. package/lib/personal-skills/service.js.map +1 -1
  108. package/lib/prompt-notes.js +58 -0
  109. package/lib/prompt-notes.js.map +1 -1
  110. package/lib/provenance/document-preview.js +10 -1
  111. package/lib/provenance/document-preview.js.map +1 -1
  112. package/lib/qa-tools/catalog.js +12 -1
  113. package/lib/qa-tools/catalog.js.map +1 -1
  114. package/lib/qa-tools/docs-tools.js +563 -0
  115. package/lib/qa-tools/docs-tools.js.map +1 -0
  116. package/lib/resolve-config.js +4 -0
  117. package/lib/resolve-config.js.map +1 -1
  118. package/lib/secure-session.js +83 -3
  119. package/lib/secure-session.js.map +1 -1
  120. package/lib/shared/integration-tokens.js +23 -0
  121. package/lib/shared/integration-tokens.js.map +1 -0
  122. package/lib/typert.host.js +1176 -213
  123. package/lib/typert.remote-client.d.ts +27 -1
  124. package/lib/typert.remote-client.js +911 -69
  125. package/lib/types/access/service.d.ts +33 -2
  126. package/lib/types/account-remotes.d.ts +30 -1
  127. package/lib/types/accounts/database.d.ts +53 -0
  128. package/lib/types/accounts/errors.d.ts +5 -1
  129. package/lib/types/accounts/service-token.d.ts +70 -0
  130. package/lib/types/accounts/store.d.ts +131 -1
  131. package/lib/types/admin/conversation-log.d.ts +12 -0
  132. package/lib/types/admin/service.d.ts +56 -1
  133. package/lib/types/admin/session-log.d.ts +21 -0
  134. package/lib/types/attachment-rules.d.ts +26 -3
  135. package/lib/types/client/QaAccountsController.d.ts +64 -5
  136. package/lib/types/client/QaSessionController.d.ts +24 -2
  137. package/lib/types/client/admin/pages/SkillFiles.d.ts +17 -0
  138. package/lib/types/client/admin/routes.d.ts +2 -0
  139. package/lib/types/client/attachments.d.ts +21 -4
  140. package/lib/types/client/lineage.d.ts +42 -0
  141. package/lib/types/client/settings/sections/notes.d.ts +5 -3
  142. package/lib/types/client/types.d.ts +71 -1
  143. package/lib/types/client/user-settings/IntegrationTokensPage.d.ts +21 -0
  144. package/lib/types/client/user-settings/PasswordSettingsPage.d.ts +19 -0
  145. package/lib/types/client/user-settings/UserSettingsDialog.d.ts +11 -2
  146. package/lib/types/client/user-settings/copy.d.ts +10 -1
  147. package/lib/types/config-resolvers/integration.d.ts +47 -0
  148. package/lib/types/config.d.ts +10 -0
  149. package/lib/types/index.d.ts +73 -1
  150. package/lib/types/integration/answer.d.ts +65 -0
  151. package/lib/types/integration/attachments.d.ts +85 -0
  152. package/lib/types/integration/contract.d.ts +196 -0
  153. package/lib/types/integration/host-runner.d.ts +44 -0
  154. package/lib/types/integration/http.d.ts +79 -0
  155. package/lib/types/integration/service.d.ts +120 -0
  156. package/lib/types/integration/transcript-reader.d.ts +100 -0
  157. package/lib/types/integration/transcript.d.ts +59 -0
  158. package/lib/types/personal-skills/index.d.ts +2 -2
  159. package/lib/types/personal-skills/paths.d.ts +15 -3
  160. package/lib/types/personal-skills/provider.d.ts +14 -2
  161. package/lib/types/personal-skills/service.d.ts +89 -14
  162. package/lib/types/prompt-notes.d.ts +28 -0
  163. package/lib/types/provenance/document-preview.d.ts +4 -0
  164. package/lib/types/qa-tools/catalog.d.ts +1 -1
  165. package/lib/types/qa-tools/docs-tools.d.ts +160 -0
  166. package/lib/types/secure-session.d.ts +52 -1
  167. package/lib/types/shared/integration-tokens.d.ts +24 -0
  168. package/lib/types/types.d.ts +170 -2
  169. package/package.json +2 -2
@@ -1,12 +1,20 @@
1
- import type { QaAccountProfileInput, QaAccountStartersInput, QaAccountUserPublic, QaOwnershipEntry, ResolvedQaSurfaceConfig } from "../types.js";
2
- import type { QaAccountsApi, StorageLike } from "./types.js";
1
+ import type { QaAccountProfileInput, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, ResolvedQaSurfaceConfig } from "../types.js";
2
+ import type { QaAccountsApi, QaIntegrationTokenResult, StorageLike } from "./types.js";
3
+ /**
4
+ * What the sign-in card says after a forgotten-password request. It names the
5
+ * request, never the account: a real address and an unknown one get the same
6
+ * sentence, which is the whole point of the endpoint.
7
+ */
8
+ export declare const QA_RESET_FILED_NOTICE = "\u0417\u0430\u044F\u0432\u043A\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443. \u0415\u0441\u043B\u0438 \u0442\u0430\u043A\u043E\u0439 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0435\u0441\u0442\u044C, \u043F\u0430\u0440\u043E\u043B\u044C \u0441\u0431\u0440\u043E\u0441\u044F\u0442 \u2014 \u0437\u0430\u0442\u0435\u043C \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0441 \u043D\u043E\u0432\u044B\u043C \u043F\u0430\u0440\u043E\u043B\u0435\u043C.";
3
9
  export type QaAccountsSnapshot = {
4
10
  readonly stage: "checking";
5
11
  } | {
6
12
  readonly stage: "gate";
7
- readonly mode: "login" | "register";
13
+ readonly mode: "login" | "register" | "reset";
8
14
  readonly busy: boolean;
9
15
  readonly error: string | null;
16
+ /** Neutral confirmation of a filed reset request; null otherwise. */
17
+ readonly notice?: string | null;
10
18
  } | {
11
19
  readonly stage: "authed";
12
20
  readonly user: QaAccountUserPublic;
@@ -67,11 +75,45 @@ export declare class QaAccountsController {
67
75
  start(): Promise<void>;
68
76
  login(email: string, password: string): Promise<void>;
69
77
  register(email: string, password: string): Promise<void>;
70
- setMode(mode: "login" | "register"): void;
78
+ setMode(mode: "login" | "register" | "reset"): void;
79
+ /**
80
+ * File a forgotten-password request for the operator queue. The card says
81
+ * the same thing whether or not the address exists — the Host refuses to
82
+ * tell, and so does this controller.
83
+ */
84
+ requestPasswordReset(email: string): Promise<void>;
85
+ /**
86
+ * Change the signed-in user's own password. The Host answers with a fresh
87
+ * token, because the write bumps the account's token version — this browser
88
+ * keeps its session only by storing that token. Resolves to audience-safe
89
+ * refusal copy, or to null once the snapshot carries the new account.
90
+ */
91
+ changePassword(currentPassword: string, nextPassword: string): Promise<string | null>;
71
92
  /** Drop the token and return to the gate (logout or expired identity). */
72
93
  signOut(): void;
73
- /** Claim one freshly created chat so the ownership map stays current. */
94
+ /**
95
+ * Claim one freshly created chat so the ownership map stays current, and
96
+ * remember it in the owned list the sidebar shows.
97
+ *
98
+ * The claim runs on every bind — a chat created now and one reopened later
99
+ * both take this path. A chat the Host reports as taken by another account
100
+ * stays out of the list: the page then holds a binding whose ownership
101
+ * belongs to someone else, and showing it is exactly what the list must not
102
+ * do. A claim that never reached the Host still records the id, because the
103
+ * page opened this chat through the attendance boundary, which claims an
104
+ * unowned chat for whoever asks first and refuses another account's — so the
105
+ * binding is this account's either way, and hiding a chat the visitor is
106
+ * looking at would be the greater lie.
107
+ */
74
108
  claimNewSession(sessionId: string): Promise<void>;
109
+ /**
110
+ * Add one chat this page holds to the owned list. The list is what the
111
+ * sidebar shows and what every cross-chat projection is scoped by, so a chat
112
+ * created or opened after login enters it here — the next login is not a
113
+ * reasonable price for a chat the visitor is looking at. Ids already known,
114
+ * and the ones an admin's cross-user view contributed, are left as they are.
115
+ */
116
+ private noteOwnedSession;
75
117
  /**
76
118
  * Replace the signed-in user's own profile. Resolves to audience-safe
77
119
  * refusal copy when the Host rejected the write, and to null once the
@@ -85,6 +127,23 @@ export declare class QaAccountsController {
85
127
  * carries the stored record, so the composer picks the new buttons up.
86
128
  */
87
129
  updateStarters(input: QaAccountStartersInput): Promise<string | null>;
130
+ /**
131
+ * The signed-in user's integration tokens. Resolves to the list, or to the
132
+ * copy the page renders when the Host refused the read; the tokens are not
133
+ * part of the account snapshot, because they are not part of the account.
134
+ */
135
+ serviceTokens(): Promise<QaIntegrationTokenResult<readonly QaServiceTokenSummary[]>>;
136
+ /**
137
+ * Mint one integration token for the signed-in user. The plaintext is in the
138
+ * answer and nowhere else — the caller has to hand it over now, because
139
+ * nothing can show it again.
140
+ */
141
+ createServiceToken(input: QaServiceTokenCreateInput): Promise<QaIntegrationTokenResult<QaIssuedServiceToken>>;
142
+ /**
143
+ * Revoke one of the signed-in user's integration tokens. The Host is the
144
+ * authority on whose token it is; a refusal is shown as it arrives.
145
+ */
146
+ revokeServiceToken(tokenId: string): Promise<QaIntegrationTokenResult<null>>;
88
147
  private authenticate;
89
148
  /** Own the account, migrate the legacy index, then project authed. */
90
149
  private enterSession;
@@ -233,8 +233,18 @@ export declare class QaSessionController {
233
233
  viewSubagent(id: string, title: string): Promise<void>;
234
234
  /** Leave the subagent transcript and re-open the chat it was launched from. */
235
235
  closeSubagent(): Promise<void>;
236
- /** This browser's chat ids: the owned list when accounts are on, else the
237
- * local index. Both intersect the host session list at projection time. */
236
+ /**
237
+ * This browser's chat ids: the account's owned chats while accounts are on,
238
+ * else the browser-local index. Either way they intersect the host session
239
+ * list at projection time.
240
+ *
241
+ * The local index is what this browser accumulated, not an identity: with
242
+ * accounts on it is the set the page *claims* at login, never one it lists.
243
+ * Unioning it in would show a chat another account started — its title, its
244
+ * timestamp, its live activity — on a shared browser, and a chat this page
245
+ * has yet to claim is added to the owned list by the claim itself, so the
246
+ * index has nothing left to supply.
247
+ */
238
248
  chatIds(): readonly string[];
239
249
  /** The bound session id, or null while no chat is bound. */
240
250
  activeSessionId(): string | null;
@@ -281,8 +291,20 @@ export declare class QaSessionController {
281
291
  * delegation's description verbatim; a child the host listed without a
282
292
  * catalog falls back to its own list title. Tolerates a host bundle older
283
293
  * than either field: an absent snapshot piece just contributes no names.
294
+ *
295
+ * Only the chats this page lists contribute: the host list is the whole
296
+ * deployment's, so an unfiltered read would hand another account's
297
+ * delegation into this browser — and a notice signed with a name that is
298
+ * not the chat's own is exactly the leak the chat list itself is scoped
299
+ * against.
284
300
  */
285
301
  private subagentNames;
302
+ /**
303
+ * The chats this page may show and name: the account's owned list while
304
+ * accounts are on, and `undefined` — every chat on the Host — for a
305
+ * deployment without accounts, where the list is the browser's own.
306
+ */
307
+ private visibleChatIds;
286
308
  private fail;
287
309
  /**
288
310
  * Attest the currently bound session. `ok` carries the session id the proof
@@ -0,0 +1,17 @@
1
+ import type { QaAdminApi } from "../../types.js";
2
+ export interface AdminSkillFilesProps {
3
+ readonly api: QaAdminApi;
4
+ readonly token: string;
5
+ }
6
+ /**
7
+ * Skill files: the deployment's shared skills, and the personal skills of one
8
+ * named account.
9
+ *
10
+ * The page is a scope picker around the very component a person edits their
11
+ * own skills with — there is no second editor, because a second editor is a
12
+ * second set of rules for what a `SKILL.md` may contain. What the console adds
13
+ * is the choice of store, the naming of its owner, and the fact that its writes
14
+ * are recorded as an administrator's.
15
+ */
16
+ export declare function AdminSkillFiles(props: AdminSkillFilesProps): import("react/jsx-runtime").JSX.Element;
17
+ //# sourceMappingURL=SkillFiles.d.ts.map
@@ -17,6 +17,8 @@ export type QaAdminRoute = {
17
17
  readonly page: "subroles";
18
18
  } | {
19
19
  readonly page: "skills";
20
+ } | {
21
+ readonly page: "skill-files";
20
22
  } | {
21
23
  readonly page: "common";
22
24
  } | {
@@ -24,14 +24,31 @@ export interface QaAttachmentLimits {
24
24
  */
25
25
  export declare function attachmentLimits(config: ResolvedQaSurfaceConfig): QaAttachmentLimits;
26
26
  /**
27
- * The file picker's `accept` value: the raster formats plus either the
28
- * configured text extensions or the browser's whole `text/*` family.
27
+ * The file picker's `accept` value: the raster formats plus either every
28
+ * configured extension and the browser's whole `text/*` family, or nothing
29
+ * when file attachments are off.
29
30
  * @param limits - attachment policy in effect.
30
31
  * @returns the accept attribute value.
31
32
  */
32
33
  export declare function attachmentAccept(limits: QaAttachmentLimits): string;
33
- /** Whether a dropped payload may be attached as a text file at all. */
34
- export declare function acceptsTextFile(name: string, mediaType: string, limits: QaAttachmentLimits): boolean;
34
+ /**
35
+ * Whether a dropped payload may be attached as a file at all. An extension the
36
+ * operator listed is enough on its own: the list is what the deployment can
37
+ * work with, and a stand whose pipeline reads Word documents lists `docx`
38
+ * rather than hoping the browser calls it text.
39
+ */
40
+ export declare function acceptsFileAttachment(name: string, mediaType: string, limits: QaAttachmentLimits): boolean;
41
+ /**
42
+ * Why one non-image file is refused, in the visitor's terms, or null when it is
43
+ * accepted. The copy used to name a fixed list ("md, txt, log и другие") that
44
+ * had nothing to do with the deployment's own setting, which is how a stand
45
+ * able to read Word documents ended up looking like a text-only one.
46
+ * @param name - display file name.
47
+ * @param mediaType - MIME type the browser reported.
48
+ * @param limits - attachment policy in effect.
49
+ * @returns the refusal message, or null.
50
+ */
51
+ export declare function fileAttachmentRefusal(name: string, mediaType: string, limits: QaAttachmentLimits): string | null;
35
52
  /**
36
53
  * Turn one dropped or picked file into a composer draft, or into the message
37
54
  * the visitor sees when the file is not accepted.
@@ -12,4 +12,46 @@ import type { SessionSummary } from "@deepseek-ai/dsh-api-session-controller/cli
12
12
  * disagree about what a chat is.
13
13
  */
14
14
  export declare function isDelegatedSession(summary: Pick<SessionSummary, "origin" | "parentId"> | undefined): boolean;
15
+ /** The session-list slice the lineage walk reads. */
16
+ export type SessionLineage = Readonly<Record<string, Pick<SessionSummary, "parentId"> | undefined>>;
17
+ /**
18
+ * The chat a session belongs to: itself when nothing delegated it, otherwise
19
+ * the root of its parent chain.
20
+ *
21
+ * A nested child names its own delegated parent in `parentId`, so one hop is
22
+ * not the chat. The walk stops at the first entry the host list does not know,
23
+ * which keeps a partially hydrated list from inventing a chat that is not
24
+ * there; a repeated id (a hostile or broken list) resolves to the id it
25
+ * repeated on instead of hanging.
26
+ */
27
+ export declare function chatRootOf(byId: SessionLineage, sessionId: string): string;
28
+ /** One delegated session a notice may be signed with, and its raw label. */
29
+ export interface SubagentNameCandidate {
30
+ readonly id: string;
31
+ /** The Host's own wording for the delegation; empty when it listed none. */
32
+ readonly label: string;
33
+ }
34
+ /** One durable delegation catalog entry, as the Host lists it. */
35
+ export interface SubagentCatalogEntry {
36
+ readonly kind?: string;
37
+ readonly id?: string;
38
+ readonly label?: string;
39
+ }
40
+ /** Direct delegation catalogs keyed by their parent chat. */
41
+ export type SubagentCatalogs = Readonly<Record<string, {
42
+ readonly entries?: readonly SubagentCatalogEntry[];
43
+ } | undefined>>;
44
+ /**
45
+ * Delegated sessions whose names a page may sign its notices with: the
46
+ * children of the chats it lists, catalog labels first and list titles after.
47
+ *
48
+ * `chats` is the visible chat set — the account's own chats while accounts are
49
+ * on, and `undefined` for a deployment without accounts, where every chat on
50
+ * the Host is the browser's own. Ownership is checked against the chat, never
51
+ * against the child: a delegated session has no owner of its own, so a child
52
+ * is visible exactly when the chat it belongs to is. Another account's chat
53
+ * therefore contributes no id and no label — a foreign chat can neither sign
54
+ * a notice nor be named by one.
55
+ */
56
+ export declare function visibleSubagentCandidates(byId: Readonly<Record<string, SessionSummary>>, catalogs: SubagentCatalogs, chats: ReadonlySet<string> | undefined): readonly SubagentNameCandidate[];
15
57
  //# sourceMappingURL=lineage.d.ts.map
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Model notes: the ambient plugin messages the injector writes into a QA chat
3
- * (who the user is, the source-provenance rules, delegation naming). Each note
4
- * has a switch and an optional wording override; an empty template keeps the
5
- * built-in text, so the fields double as a wording editor and a mute switch.
3
+ * (who the user is, the source-provenance rules, delegation naming, which
4
+ * reader an attached document belongs to, where an answer should come from).
5
+ * Each note has a switch and an optional wording override; an empty template
6
+ * keeps the built-in text, so the fields double as a wording editor and a mute
7
+ * switch.
6
8
  */
7
9
  import { type ConfigProps } from "./common.js";
8
10
  /** The ambient notes the Host writes into QA chats as hidden user messages. */
@@ -1,7 +1,7 @@
1
1
  import type { ClientRemote } from "@deepseek-ai/dsh-api-gateway/client";
2
2
  import type { ISessions, SessionFace } from "@deepseek-ai/dsh-api-session-controller/client";
3
3
  import type { UiConversation } from "@deepseek-ai/dsh-client-ui-conversation/client";
4
- import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountStarter, QaAccountUserPublic, QaAccessAdminSnapshot, QaApprovalDecision, QaClaimResult, QaLockdownProof, QaOwnershipEntry, QaPendingApproval, QaPendingQuestion, QaQuestionAnswerItem, QaSessionState, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillSummary, QaSkillToolDescriptor, QaSkillValidation, QaSourceFilePreview, QaTurnSources, QaWhoamiResult, QaDocumentPreview, QaWorkspaceFile, QaWorkspaceListing, QaCapabilitySelection, QaCurrentAccess, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaSlashCatalog, QaSlashExecution, QaSlashSubmitAttachment, QaUserQuery } from "../types.js";
4
+ import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountStarter, QaAccountUserPublic, QaAccessAdminSnapshot, QaApprovalDecision, QaClaimResult, QaLockdownProof, QaOwnershipEntry, QaPasswordResetRequest, QaPendingApproval, QaPendingQuestion, QaQuestionAnswerItem, QaIssuedServiceToken, QaServiceTokenCreateInput, QaServiceTokenSummary, QaSessionState, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillSummary, QaSkillToolDescriptor, QaSkillValidation, QaSourceFilePreview, QaTurnSources, QaWhoamiResult, QaDocumentPreview, QaWorkspaceFile, QaWorkspaceListing, QaCapabilitySelection, QaCurrentAccess, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminSkillScope, QaAdminSkillsView, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaSlashCatalog, QaSlashExecution, QaSlashSubmitAttachment, QaUserQuery } from "../types.js";
5
5
  /** Wire content one prompt carries: text plus base64 image uploads. */
6
6
  export type QaPromptContent = Parameters<SessionFace["prompt"]>[0];
7
7
  /** DSH session runtime surface the QA controller is allowed to touch. */
@@ -56,6 +56,16 @@ export interface QaAdminApi {
56
56
  users(token: string, query: QaUserQuery, cursor: string | null, limit: number | null): Promise<RemoteResult<QaAdminPage<QaAdminUserRow>>>;
57
57
  user(token: string, userId: string): Promise<RemoteResult<QaAdminUserDetail>>;
58
58
  updateUser(token: string, userId: string, update: QaAdminUserUpdate): Promise<RemoteResult<QaAdminUserDetail>>;
59
+ /**
60
+ * Accounts that asked for a password reset from the sign-in screen, newest
61
+ * first. Reading takes `users.read`; answering one takes `users.manage`.
62
+ */
63
+ passwordResetRequests(token: string): Promise<RemoteResult<readonly QaPasswordResetRequest[]>>;
64
+ /**
65
+ * Answer one request: set that account's new password and drop its row. The
66
+ * account's live sessions end with the token-version bump.
67
+ */
68
+ resetPassword(token: string, userId: string, password: string): Promise<RemoteResult<QaAdminUserDetail>>;
59
69
  conversations(token: string, query: QaConversationQuery, cursor: string | null, limit: number | null): Promise<RemoteResult<QaAdminPage<QaConversationSummary>>>;
60
70
  conversation(token: string, conversationId: string): Promise<RemoteResult<QaConversationDetail>>;
61
71
  /**
@@ -71,6 +81,18 @@ export interface QaAdminApi {
71
81
  saveReview(token: string, input: QaConversationReviewInput): Promise<RemoteResult<QaConversationReview>>;
72
82
  metrics(token: string): Promise<RemoteResult<QaQualityMetrics>>;
73
83
  audit(token: string, query: QaAuditQuery, cursor: string | null, limit: number | null): Promise<RemoteResult<QaAdminPage<QaAdminAuditEvent>>>;
84
+ /**
85
+ * Skill files an administrator may edit: the deployment's shared store, or
86
+ * one named account's own. These are the same files the owner edits from
87
+ * their settings dialog, opened here with the authorization to leave a mark
88
+ * the owner sees.
89
+ */
90
+ skills(token: string, scope: QaAdminSkillScope): Promise<RemoteResult<QaAdminSkillsView>>;
91
+ skill(token: string, scope: QaAdminSkillScope, name: string): Promise<RemoteResult<QaSkillDocument>>;
92
+ saveSkill(token: string, scope: QaAdminSkillScope, name: string | null, input: QaSkillDraftInput): Promise<RemoteResult<QaSkillDocument>>;
93
+ deleteSkill(token: string, scope: QaAdminSkillScope, name: string, expectedRevision: string | null): Promise<RemoteResult<QaSkillRemoval>>;
94
+ validateSkill(token: string, scope: QaAdminSkillScope, name: string | null, input: QaSkillDraftInput): Promise<RemoteResult<QaSkillValidation>>;
95
+ skillTools(token: string, scope: QaAdminSkillScope): Promise<RemoteResult<readonly QaSkillToolDescriptor[]>>;
74
96
  }
75
97
  /**
76
98
  * Browser file-upload service (`ctx.fileUpload`), described structurally on
@@ -263,6 +285,31 @@ export interface QaBoundSkillApi {
263
285
  tools(): Promise<RemoteResult<readonly QaSkillToolDescriptor[]>>;
264
286
  validate(name: string | null, input: QaSkillDraftInput): Promise<RemoteResult<QaSkillValidation>>;
265
287
  }
288
+ /**
289
+ * Outcome of one integration-token operation as the settings page sees it:
290
+ * the value, or the audience-safe copy for the refusal. The page renders the
291
+ * copy and never has to know a reason code.
292
+ */
293
+ export type QaIntegrationTokenResult<Value> = {
294
+ readonly ok: true;
295
+ readonly value: Value;
296
+ } | {
297
+ readonly ok: false;
298
+ readonly error: string;
299
+ };
300
+ /** The integration-token section with the account token bound at the call site. */
301
+ export interface QaIntegrationTokenApi {
302
+ /**
303
+ * Whether this deployment can mint at all. A token is a credential for the
304
+ * HTTP API, so when that API is off the page explains the state instead of
305
+ * offering a button that only ever refuses — while listing and revoking stay
306
+ * available, because a credential has to remain revocable.
307
+ */
308
+ readonly canCreate: boolean;
309
+ list(): Promise<QaIntegrationTokenResult<readonly QaServiceTokenSummary[]>>;
310
+ create(input: QaServiceTokenCreateInput): Promise<QaIntegrationTokenResult<QaIssuedServiceToken>>;
311
+ revoke(tokenId: string): Promise<QaIntegrationTokenResult<null>>;
312
+ }
266
313
  /** Account remotes exposed by the plugin's own typert namespace. */
267
314
  export interface QaAccountsApi {
268
315
  accountsWhoami(token: string): Promise<RemoteResult<QaWhoamiResult>>;
@@ -279,6 +326,29 @@ export interface QaAccountsApi {
279
326
  accountsUpdateProfile(token: string, input: QaAccountProfileInput): Promise<RemoteResult<QaAccountUserPublic>>;
280
327
  /** Replace the caller's own starter buttons; the token is the identity. */
281
328
  accountsUpdateStarters(token: string, input: QaAccountStartersInput): Promise<RemoteResult<QaAccountUserPublic>>;
329
+ /**
330
+ * Replace the caller's own password. The answer carries a fresh token: the
331
+ * write bumps the account's token version, so without it the browser that
332
+ * made the change would sign itself out.
333
+ */
334
+ accountsChangePassword(token: string, currentPassword: string, nextPassword: string): Promise<RemoteResult<QaAccountSession>>;
335
+ /**
336
+ * File a forgotten-password request for the operator queue. The answer is
337
+ * the same for every address, so it reveals nothing about the account.
338
+ */
339
+ accountsRequestPasswordReset(email: string): Promise<RemoteResult<{
340
+ readonly accepted: true;
341
+ }>>;
342
+ /** The caller's own integration tokens; never a secret. */
343
+ accountsListServiceTokens(token: string): Promise<RemoteResult<{
344
+ readonly tokens: readonly QaServiceTokenSummary[];
345
+ }>>;
346
+ /** Mint one integration token for the caller; the plaintext comes back once. */
347
+ accountsCreateServiceToken(token: string, input: QaServiceTokenCreateInput): Promise<RemoteResult<QaIssuedServiceToken>>;
348
+ /** Revoke one of the caller's own integration tokens. */
349
+ accountsRevokeServiceToken(token: string, tokenId: string): Promise<RemoteResult<{
350
+ readonly revoked: boolean;
351
+ }>>;
282
352
  }
283
353
  /** One button above an empty composer: what it reads and what it sends. */
284
354
  export type QaQuickQuestion = QaAccountStarter;
@@ -0,0 +1,21 @@
1
+ import type { QaIntegrationTokenApi } from "../types.js";
2
+ /**
3
+ * The integration tokens of the signed-in account: mint, read the secret once,
4
+ * and revoke.
5
+ *
6
+ * An integration token is the credential a non-browser application presents to
7
+ * the QA HTTP API, and it is deliberately a second, separate credential from
8
+ * the browser session: it survives a password change, it carries its own
9
+ * scopes and expiry, and it is revoked here rather than by signing anybody out.
10
+ * The page therefore says what the token is *for* before it offers to mint one
11
+ * — handing a long-lived key to a service is not a preference toggle.
12
+ *
13
+ * The plaintext exists in exactly one place: the answer that minted it. It is
14
+ * shown once, with the warning that this is the only time, and the list that
15
+ * follows carries no secret in any state — a list that could repeat a
16
+ * credential would be a list that leaks it.
17
+ */
18
+ export declare function QaIntegrationTokensPage(props: {
19
+ readonly api: QaIntegrationTokenApi;
20
+ }): import("react/jsx-runtime").JSX.Element;
21
+ //# sourceMappingURL=IntegrationTokensPage.d.ts.map
@@ -0,0 +1,19 @@
1
+ export interface QaPasswordSettingsPageProps {
2
+ /**
3
+ * Replace the signed-in user's password; resolves to refusal copy, or to
4
+ * null once the Host accepted it. The controller keeps this browser signed
5
+ * in with the token that answer carries.
6
+ */
7
+ readonly onChange: (currentPassword: string, nextPassword: string) => Promise<string | null>;
8
+ }
9
+ /**
10
+ * The password section of the settings dialog: current, new, repeat. The
11
+ * repeat field is client-side only — it exists to catch a typo in the field
12
+ * that, unlike a name, cannot be read back later.
13
+ *
14
+ * The consequence is stated rather than discovered: changing a password ends
15
+ * every *other* session of this account, which is exactly the point after a
16
+ * leak and exactly what looks like a bug when nobody said so.
17
+ */
18
+ export declare function QaPasswordSettingsPage(props: QaPasswordSettingsPageProps): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=PasswordSettingsPage.d.ts.map
@@ -1,8 +1,8 @@
1
1
  import type { QaAccountIdentityField, QaAccountProfile, QaAccountProfileInput, QaAccountStarters, QaAccountStartersInput } from "../../types.js";
2
- import type { QaBoundSkillApi } from "../types.js";
2
+ import type { QaBoundSkillApi, QaIntegrationTokenApi } from "../types.js";
3
3
  import type { QaUserSettingsSections } from "../settings-extensions/index.js";
4
4
  /** Sections of the user-facing settings dialog. */
5
- export type QaSettingsSectionId = "profile" | "starters" | "general" | "skills" | (string & {});
5
+ export type QaSettingsSectionId = "profile" | "password" | "starters" | "tokens" | "general" | "skills" | (string & {});
6
6
  export interface QaUserSettingsDialogProps {
7
7
  readonly open: boolean;
8
8
  /** Section to show when the dialog opens. */
@@ -18,11 +18,20 @@ export interface QaUserSettingsDialogProps {
18
18
  readonly instructionsMaxLength: number;
19
19
  readonly onSave: (input: QaAccountProfileInput) => Promise<string | null>;
20
20
  };
21
+ /**
22
+ * Self-service password change. Absent only where the surface runs without
23
+ * accounts at all, in which case this dialog is not shown either.
24
+ */
25
+ readonly password?: {
26
+ readonly onChange: (currentPassword: string, nextPassword: string) => Promise<string | null>;
27
+ };
21
28
  /** Self-service starter buttons; absent when the deployment turns them off. */
22
29
  readonly starters?: {
23
30
  readonly starters: QaAccountStarters;
24
31
  readonly onSave: (input: QaAccountStartersInput) => Promise<string | null>;
25
32
  };
33
+ /** Integration tokens; absent when accounts are off altogether. */
34
+ readonly integrationTokens?: QaIntegrationTokenApi;
26
35
  /** Personal skills; absent when the deployment cannot host them. */
27
36
  readonly skills?: QaBoundSkillApi;
28
37
  /** Additive pages contributed by separately shipped QA plugins. */
@@ -1,4 +1,4 @@
1
- import type { QaSkillDiagnostic } from "../../types.js";
1
+ import type { QaSkillAdminEdit, QaSkillDiagnostic } from "../../types.js";
2
2
  /**
3
3
  * Audience-facing copy for the settings dialog. The Host sends stable codes
4
4
  * and never Russian prose, so every message a user reads is written here.
@@ -11,6 +11,15 @@ export declare function diagnosticMessage(diagnostic: QaSkillDiagnostic): string
11
11
  * fault, an unknown code — reads as the generic failure.
12
12
  */
13
13
  export declare function skillFailureCopy(error: unknown): string;
14
+ /**
15
+ * The badge line for a skill an administrator last wrote.
16
+ *
17
+ * It names the role, not the person: the row already carries a date, and who
18
+ * administers a deployment is not a fact a personal settings page should
19
+ * depend on. The mark itself comes from the Host, which only reports one while
20
+ * the stored bytes are still the administrator's.
21
+ */
22
+ export declare function adminEditMessage(edit: QaSkillAdminEdit): string;
14
23
  /** Whether a failure is the optimistic-concurrency refusal. */
15
24
  export declare function isSkillConflict(error: unknown): boolean;
16
25
  export declare const SKILLS_EMPTY_COPY: {
@@ -0,0 +1,47 @@
1
+ import type { QaIntegrationConfig, ResolvedQaSurfaceConfig } from "../types.js";
2
+ type ResolvedIntegration = ResolvedQaSurfaceConfig["integration"];
3
+ /** Operator-tunable bounds; the values the settings card shows as hard limits. */
4
+ export declare const QA_INTEGRATION_TTL_DAYS_MIN = 1;
5
+ export declare const QA_INTEGRATION_TTL_DAYS_MAX = 3650;
6
+ export declare const QA_INTEGRATION_TIMEOUT_MS_MIN = 5000;
7
+ export declare const QA_INTEGRATION_TIMEOUT_MS_MAX = 600000;
8
+ export declare const QA_INTEGRATION_CONCURRENCY_MIN = 1;
9
+ export declare const QA_INTEGRATION_CONCURRENCY_MAX = 16;
10
+ export declare const QA_INTEGRATION_RATE_MIN = 1;
11
+ export declare const QA_INTEGRATION_RATE_MAX = 600;
12
+ export declare const QA_INTEGRATION_BODY_BYTES_MIN = 1048576;
13
+ export declare const QA_INTEGRATION_BODY_BYTES_MAX = 67108864;
14
+ export declare const QA_INTEGRATION_ATTACHMENT_BYTES_MIN = 1024;
15
+ export declare const QA_INTEGRATION_ANSWER_CHARS_MIN = 256;
16
+ export declare const QA_INTEGRATION_ANSWER_CHARS_MAX = 65536;
17
+ /**
18
+ * Normalize the integration API's base path.
19
+ *
20
+ * It is a route namespace rather than a page: it may live under `/qa` (the
21
+ * default does), because the web server tries its exact table before the QA
22
+ * navigation's prefix, so `/qa/api/ask` never falls into the page route. What
23
+ * it may not do is claim the operator root, the first-party `/api` namespace
24
+ * or the plugin asset namespace.
25
+ * @param value - the configured path.
26
+ * @returns the path without a trailing slash.
27
+ */
28
+ export declare function normalizeIntegrationBasePath(value: string): string;
29
+ /**
30
+ * The integration API's slice of the resolved configuration.
31
+ *
32
+ * The endpoint is off unless a deployment asks for it: it is a network
33
+ * surface reachable by anything that holds a token, and a QA stand that never
34
+ * integrates anything must not grow one by accident.
35
+ *
36
+ * It also needs accounts. Integration tokens are issued to an account and
37
+ * verified against it, so a deployment with `accounts.enabled: false` has
38
+ * nothing to authenticate a caller with; switching the API on there is a
39
+ * configuration mistake, and it is refused rather than served open.
40
+ *
41
+ * @param input - raw integration config.
42
+ * @param accounts - the resolved accounts domain, for the cross-check.
43
+ * @returns the frozen resolved slice.
44
+ */
45
+ export declare function resolveIntegration(input: QaIntegrationConfig | undefined, accounts: ResolvedQaSurfaceConfig["accounts"]): ResolvedIntegration;
46
+ export {};
47
+ //# sourceMappingURL=integration.d.ts.map
@@ -1,5 +1,15 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import type { QaSurfaceConfig } from "./types.js";
3
+ /**
4
+ * Version this build reports to the integration API's health endpoint.
5
+ *
6
+ * Read from the package manifest rather than written as a constant, so a
7
+ * release bump cannot drift away from it. Resolved on first use so importing
8
+ * the module never touches the filesystem, and this module is the right home
9
+ * for it: it is bundled one level under the package root, which is the depth
10
+ * `../package.json` resolves at.
11
+ */
12
+ export declare function qaSurfaceVersion(): string;
3
13
  export declare const ConfigSchema: z<QaSurfaceConfig>;
4
14
  export { DEFAULT_QA_SURFACE_CONFIG, normalizeRoutePath, resolveConfig, } from "./resolve-config.js";
5
15
  //# sourceMappingURL=config.d.ts.map