@yadsh/dsh-qa-surface 0.7.4 → 0.9.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 (247) hide show
  1. package/README.md +169 -12
  2. package/compatibility.json +5 -0
  3. package/lib/access/capability-catalog.js +32 -3
  4. package/lib/access/capability-catalog.js.map +1 -1
  5. package/lib/access/model.js +24 -4
  6. package/lib/access/model.js.map +1 -1
  7. package/lib/access/role-repository.js +199 -91
  8. package/lib/access/role-repository.js.map +1 -1
  9. package/lib/access/service.js +223 -7
  10. package/lib/access/service.js.map +1 -1
  11. package/lib/account-remotes.js +2 -4
  12. package/lib/account-remotes.js.map +1 -1
  13. package/lib/accounts/database.js +447 -0
  14. package/lib/accounts/database.js.map +1 -0
  15. package/lib/accounts/errors.js.map +1 -1
  16. package/lib/accounts/file.js +27 -90
  17. package/lib/accounts/file.js.map +1 -1
  18. package/lib/accounts/store.js +224 -59
  19. package/lib/accounts/store.js.map +1 -1
  20. package/lib/admin/permissions.js +4 -0
  21. package/lib/admin/permissions.js.map +1 -1
  22. package/lib/admin/quality-store.js +270 -54
  23. package/lib/admin/quality-store.js.map +1 -1
  24. package/lib/admin/service.js +145 -7
  25. package/lib/admin/service.js.map +1 -1
  26. package/lib/admin/session-log.js +66 -5
  27. package/lib/admin/session-log.js.map +1 -1
  28. package/lib/cli.js +2 -1
  29. package/lib/cli.js.map +1 -1
  30. package/lib/client/QaSessionController.js +379 -39
  31. package/lib/client/QaSessionController.js.map +1 -1
  32. package/lib/client/QaSurface.js +94 -34
  33. package/lib/client/QaSurface.js.map +1 -1
  34. package/lib/client/QaTranscriptAdapter.js +40 -0
  35. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  36. package/lib/client/admin/QaAdmin.js +26 -8
  37. package/lib/client/admin/QaAdmin.js.map +1 -1
  38. package/lib/client/admin/{format.js → copy.js} +1 -1
  39. package/lib/client/admin/copy.js.map +1 -0
  40. package/lib/client/admin/pages/Conversations.js +34 -8
  41. package/lib/client/admin/pages/Conversations.js.map +1 -1
  42. package/lib/client/admin/pages/Overview.js +1 -1
  43. package/lib/client/admin/pages/Overview.js.map +1 -1
  44. package/lib/client/admin/pages/Quality.js +1 -1
  45. package/lib/client/admin/pages/Quality.js.map +1 -1
  46. package/lib/client/admin/pages/Review.js +1 -1
  47. package/lib/client/admin/pages/Review.js.map +1 -1
  48. package/lib/client/admin/pages/Users.js +6 -2
  49. package/lib/client/admin/pages/Users.js.map +1 -1
  50. package/lib/client/admin/shared.js +10 -1
  51. package/lib/client/admin/shared.js.map +1 -1
  52. package/lib/client/admin/styles.js +2 -0
  53. package/lib/client/admin/styles.js.map +1 -1
  54. package/lib/client/audit/QaAuditDialog.js +74 -0
  55. package/lib/client/audit/QaAuditDialog.js.map +1 -0
  56. package/lib/client/audit/QaAuditRowBadge.js +17 -0
  57. package/lib/client/audit/QaAuditRowBadge.js.map +1 -0
  58. package/lib/client/audit/controller.js +39 -0
  59. package/lib/client/audit/controller.js.map +1 -0
  60. package/lib/client/audit/types.js +36 -0
  61. package/lib/client/audit/types.js.map +1 -0
  62. package/lib/client/audit/use-chat-audits.js +139 -0
  63. package/lib/client/audit/use-chat-audits.js.map +1 -0
  64. package/lib/client/components/Markdown.js +14 -206
  65. package/lib/client/components/Markdown.js.map +1 -1
  66. package/lib/client/components/QaChangelog.js +91 -0
  67. package/lib/client/components/QaChangelog.js.map +1 -1
  68. package/lib/client/components/QaComposer.js +63 -3
  69. package/lib/client/components/QaComposer.js.map +1 -1
  70. package/lib/client/components/QaHeader.js +1 -3
  71. package/lib/client/components/QaHeader.js.map +1 -1
  72. package/lib/client/components/QaMessage.js +25 -4
  73. package/lib/client/components/QaMessage.js.map +1 -1
  74. package/lib/client/components/QaQuestions.js +1 -1
  75. package/lib/client/components/QaQuestions.js.map +1 -1
  76. package/lib/client/components/QaSidebar.js +17 -3
  77. package/lib/client/components/QaSidebar.js.map +1 -1
  78. package/lib/client/components/QaSourcesPanel.js +3 -18
  79. package/lib/client/components/QaSourcesPanel.js.map +1 -1
  80. package/lib/client/components/QaWorkGroup.js +4 -8
  81. package/lib/client/components/QaWorkGroup.js.map +1 -1
  82. package/lib/client/components/source-icon.js +26 -0
  83. package/lib/client/components/source-icon.js.map +1 -0
  84. package/lib/client/components/thinking-phrases.js +2 -11
  85. package/lib/client/components/thinking-phrases.js.map +1 -1
  86. package/lib/client/components/use-now.js +21 -0
  87. package/lib/client/components/use-now.js.map +1 -0
  88. package/lib/client/index.js +33 -5
  89. package/lib/client/index.js.map +1 -1
  90. package/lib/client/lineage.js +18 -0
  91. package/lib/client/lineage.js.map +1 -0
  92. package/lib/client/markdown/CodeBlock.js +28 -0
  93. package/lib/client/markdown/CodeBlock.js.map +1 -0
  94. package/lib/client/markdown/blocks.js +342 -0
  95. package/lib/client/markdown/blocks.js.map +1 -0
  96. package/lib/client/markdown/highlight.js +1089 -0
  97. package/lib/client/markdown/highlight.js.map +1 -0
  98. package/lib/client/markdown/inline.js +377 -0
  99. package/lib/client/markdown/inline.js.map +1 -0
  100. package/lib/client/markdown/render.js +147 -0
  101. package/lib/client/markdown/render.js.map +1 -0
  102. package/lib/client/markdown/source-chip.js +55 -0
  103. package/lib/client/markdown/source-chip.js.map +1 -0
  104. package/lib/client/markdown/use-transient-flag.js +27 -0
  105. package/lib/client/markdown/use-transient-flag.js.map +1 -0
  106. package/lib/client/project-session-state.js +15 -2
  107. package/lib/client/project-session-state.js.map +1 -1
  108. package/lib/client/prompt-content.js +26 -0
  109. package/lib/client/prompt-content.js.map +1 -1
  110. package/lib/client/role/RoleSelector.js +11 -2
  111. package/lib/client/role/RoleSelector.js.map +1 -1
  112. package/lib/client/role/preview.js +123 -0
  113. package/lib/client/role/preview.js.map +1 -0
  114. package/lib/client/settings/card.js +2 -2
  115. package/lib/client/settings/card.js.map +1 -1
  116. package/lib/client/settings/sections/lockdown.js +1 -6
  117. package/lib/client/settings/sections/lockdown.js.map +1 -1
  118. package/lib/client/settings/sections/slash.js +57 -0
  119. package/lib/client/settings/sections/slash.js.map +1 -0
  120. package/lib/client/settings/sections/status.js +9 -1
  121. package/lib/client/settings/sections/status.js.map +1 -1
  122. package/lib/client/settings/sections.js +1 -0
  123. package/lib/client/settings/sections.js.map +1 -1
  124. package/lib/client/slash/SlashPalette.js +48 -0
  125. package/lib/client/slash/SlashPalette.js.map +1 -0
  126. package/lib/client/slash/copy.js +54 -0
  127. package/lib/client/slash/copy.js.map +1 -0
  128. package/lib/client/slash/slash-router.js +49 -0
  129. package/lib/client/slash/slash-router.js.map +1 -0
  130. package/lib/client/slash/useSlashNavigation.js +95 -0
  131. package/lib/client/slash/useSlashNavigation.js.map +1 -0
  132. package/lib/client/styles.js +124 -20
  133. package/lib/client/styles.js.map +1 -1
  134. package/lib/client/types.js +11 -0
  135. package/lib/client/types.js.map +1 -1
  136. package/lib/client.js +7615 -1567
  137. package/lib/client.js.map +1 -1
  138. package/lib/config-resolvers/accounts.js +15 -0
  139. package/lib/config-resolvers/accounts.js.map +1 -1
  140. package/lib/config-resolvers/basics.js +11 -2
  141. package/lib/config-resolvers/basics.js.map +1 -1
  142. package/lib/config-resolvers/defaults.js +30 -0
  143. package/lib/config-resolvers/defaults.js.map +1 -1
  144. package/lib/config-resolvers/lockdown.js +8 -2
  145. package/lib/config-resolvers/lockdown.js.map +1 -1
  146. package/lib/config-resolvers/slash-commands.js +60 -0
  147. package/lib/config-resolvers/slash-commands.js.map +1 -0
  148. package/lib/config-resolvers/sources.js +16 -0
  149. package/lib/config-resolvers/sources.js.map +1 -1
  150. package/lib/config.js +118 -2
  151. package/lib/config.js.map +1 -1
  152. package/lib/index.js +183 -11
  153. package/lib/index.js.map +1 -1
  154. package/lib/lockdown-policy.js +19 -0
  155. package/lib/lockdown-policy.js.map +1 -1
  156. package/lib/personal-skills/service.js +16 -3
  157. package/lib/personal-skills/service.js.map +1 -1
  158. package/lib/provenance/host-store.js +25 -4
  159. package/lib/provenance/host-store.js.map +1 -1
  160. package/lib/provenance/retention.js +25 -0
  161. package/lib/provenance/retention.js.map +1 -0
  162. package/lib/provenance/snapshot-store.js +290 -35
  163. package/lib/provenance/snapshot-store.js.map +1 -1
  164. package/lib/questions.js +98 -38
  165. package/lib/questions.js.map +1 -1
  166. package/lib/resolve-config.js +5 -0
  167. package/lib/resolve-config.js.map +1 -1
  168. package/lib/secure-session.js +139 -21
  169. package/lib/secure-session.js.map +1 -1
  170. package/lib/slash/catalog.js +77 -0
  171. package/lib/slash/catalog.js.map +1 -0
  172. package/lib/slash/matcher.js +100 -0
  173. package/lib/slash/matcher.js.map +1 -0
  174. package/lib/slash/parser.js +79 -0
  175. package/lib/slash/parser.js.map +1 -0
  176. package/lib/slash/policy.js +72 -0
  177. package/lib/slash/policy.js.map +1 -0
  178. package/lib/slash/remotes.js +272 -0
  179. package/lib/slash/remotes.js.map +1 -0
  180. package/lib/typert.host.js +348 -56
  181. package/lib/typert.remote-client.d.ts +7 -1
  182. package/lib/typert.remote-client.js +274 -51
  183. package/lib/types/access/capability-catalog.d.ts +28 -1
  184. package/lib/types/access/role-repository.d.ts +38 -7
  185. package/lib/types/access/service.d.ts +106 -3
  186. package/lib/types/account-remotes.d.ts +8 -4
  187. package/lib/types/accounts/database.d.ts +84 -0
  188. package/lib/types/accounts/errors.d.ts +7 -1
  189. package/lib/types/accounts/file.d.ts +17 -39
  190. package/lib/types/accounts/store.d.ts +110 -19
  191. package/lib/types/admin/quality-store.d.ts +38 -2
  192. package/lib/types/admin/service.d.ts +34 -2
  193. package/lib/types/admin/session-log.d.ts +65 -1
  194. package/lib/types/client/QaSessionController.d.ts +84 -2
  195. package/lib/types/client/QaSurface.d.ts +14 -1
  196. package/lib/types/client/admin/{format.d.ts → copy.d.ts} +1 -1
  197. package/lib/types/client/admin/pages/Conversations.d.ts +4 -0
  198. package/lib/types/client/audit/QaAuditDialog.d.ts +21 -0
  199. package/lib/types/client/audit/QaAuditRowBadge.d.ts +18 -0
  200. package/lib/types/client/audit/controller.d.ts +27 -0
  201. package/lib/types/client/audit/types.d.ts +72 -0
  202. package/lib/types/client/audit/use-chat-audits.d.ts +20 -0
  203. package/lib/types/client/components/Markdown.d.ts +7 -8
  204. package/lib/types/client/components/QaComposer.d.ts +12 -2
  205. package/lib/types/client/components/QaQuestions.d.ts +7 -0
  206. package/lib/types/client/components/QaSidebar.d.ts +11 -1
  207. package/lib/types/client/components/QaSourcesPanel.d.ts +1 -3
  208. package/lib/types/client/components/source-icon.d.ts +12 -0
  209. package/lib/types/client/components/use-now.d.ts +8 -0
  210. package/lib/types/client/lineage.d.ts +15 -0
  211. package/lib/types/client/markdown/CodeBlock.d.ts +14 -0
  212. package/lib/types/client/markdown/blocks.d.ts +74 -0
  213. package/lib/types/client/markdown/highlight.d.ts +29 -0
  214. package/lib/types/client/markdown/inline.d.ts +40 -0
  215. package/lib/types/client/markdown/render.d.ts +13 -0
  216. package/lib/types/client/markdown/source-chip.d.ts +32 -0
  217. package/lib/types/client/markdown/use-transient-flag.d.ts +12 -0
  218. package/lib/types/client/project-session-state.d.ts +3 -1
  219. package/lib/types/client/prompt-content.d.ts +9 -1
  220. package/lib/types/client/role/RoleSelector.d.ts +11 -1
  221. package/lib/types/client/role/preview.d.ts +109 -0
  222. package/lib/types/client/settings/sections/slash.d.ts +16 -0
  223. package/lib/types/client/settings/sections.d.ts +1 -0
  224. package/lib/types/client/slash/SlashPalette.d.ts +24 -0
  225. package/lib/types/client/slash/copy.d.ts +30 -0
  226. package/lib/types/client/slash/slash-router.d.ts +67 -0
  227. package/lib/types/client/slash/useSlashNavigation.d.ts +47 -0
  228. package/lib/types/client/styles.d.ts +9 -0
  229. package/lib/types/client/types.d.ts +16 -1
  230. package/lib/types/config-resolvers/slash-commands.d.ts +20 -0
  231. package/lib/types/index.d.ts +83 -2
  232. package/lib/types/lockdown-policy.d.ts +16 -0
  233. package/lib/types/personal-skills/service.d.ts +5 -0
  234. package/lib/types/provenance/host-store.d.ts +20 -3
  235. package/lib/types/provenance/retention.d.ts +46 -0
  236. package/lib/types/provenance/snapshot-store.d.ts +63 -7
  237. package/lib/types/questions.d.ts +13 -0
  238. package/lib/types/secure-session.d.ts +27 -1
  239. package/lib/types/slash/catalog.d.ts +56 -0
  240. package/lib/types/slash/matcher.d.ts +51 -0
  241. package/lib/types/slash/parser.d.ts +46 -0
  242. package/lib/types/slash/policy.d.ts +40 -0
  243. package/lib/types/slash/remotes.d.ts +35 -0
  244. package/lib/types/types.d.ts +254 -5
  245. package/package.json +9 -5
  246. package/scripts/attach-workspace-sessions.mjs +472 -0
  247. package/lib/client/admin/format.js.map +0 -1
@@ -20,6 +20,8 @@ export interface QaManualQueueEntry {
20
20
  readonly createdAt: string;
21
21
  }
22
22
  export declare function defaultQualityFilePath(): string;
23
+ /** The pre-SQLite quality file, imported once on first use. */
24
+ export declare function defaultLegacyQualityFilePath(): string;
23
25
  /**
24
26
  * Atomic, externally reloadable store for the quality layer. Writes are
25
27
  * synchronous and persisted with a temp file plus rename, matching the accounts
@@ -33,9 +35,11 @@ export declare function defaultQualityFilePath(): string;
33
35
  */
34
36
  export declare class QaQualityStore {
35
37
  readonly filePath: string;
38
+ private readonly storage;
36
39
  private file;
37
- private fileStamp;
40
+ private observedDataVersion;
38
41
  constructor(filePath?: string);
42
+ close(): void;
39
43
  /**
40
44
  * Record or replace one user's rating of one message. Re-rating the same
41
45
  * message overwrites in place rather than appending: a reviewer reads the
@@ -63,6 +67,24 @@ export declare class QaQualityStore {
63
67
  /** Drop a manual queue entry once a reviewer has answered it. */
64
68
  dequeueReview(conversationId: string, messageId?: string): boolean;
65
69
  manualQueue(): readonly QaManualQueueEntry[];
70
+ /**
71
+ * Drop every quality record of the named conversations.
72
+ *
73
+ * A record is keyed by the conversation it judges, and nothing else removes
74
+ * it: a chat deleted from the Harness would otherwise leave its ratings,
75
+ * reviews and queue entries behind, still counted by the metrics and still
76
+ * pointing at a conversation the console cannot open. The ownership sweep is
77
+ * the one place that learns a chat is gone, and it calls this with the ids
78
+ * it reclaimed.
79
+ *
80
+ * The audit trail is not touched: it records what administrators did, not
81
+ * what a conversation held, and a housekeeping drop is not an administrative
82
+ * act to answer for.
83
+ *
84
+ * @param conversationIds - the conversations to forget.
85
+ * @returns how many rows were removed.
86
+ */
87
+ dropConversations(conversationIds: readonly string[]): number;
66
88
  /** Append one administrative action to the audit trail. */
67
89
  appendAudit(input: {
68
90
  readonly actorId: string;
@@ -75,8 +97,22 @@ export declare class QaQualityStore {
75
97
  auditEvents(): readonly QaAdminAuditEvent[];
76
98
  /** Serialize one before/after value, refusing what no reader could use. */
77
99
  private snapshot;
100
+ /** Write one record, moving it to the end of its family's order. */
101
+ private upsertRow;
102
+ /** Append a record whose identity the store mints itself. */
103
+ private appendRow;
104
+ private deleteRow;
105
+ private applyCap;
106
+ private rowsOf;
107
+ private readDataVersion;
78
108
  private reload;
79
109
  private load;
80
- private persist;
110
+ /**
111
+ * Import a pre-SQLite `qa-quality.json` exactly once, then rename it aside.
112
+ * A row the database already holds wins, so a leftover file cannot overwrite
113
+ * a verdict a person has since given.
114
+ */
115
+ private importLegacyFile;
116
+ private assertImportArrived;
81
117
  }
82
118
  //# sourceMappingURL=quality-store.d.ts.map
@@ -2,10 +2,10 @@ import type { PluginLogger } from "@yadsh/dsh-plugin-log";
2
2
  import type { QaAccounts } from "../accounts/store.js";
3
3
  import type { QaAccessService } from "../access/service.js";
4
4
  import type { QaRoleRepository } from "../access/role-repository.js";
5
- import type { QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaUserQuery } from "../types.js";
5
+ import type { QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaUserQuery } from "../types.js";
6
6
  import type { QaQualityStore } from "./quality-store.js";
7
7
  import type { QaAdminRedactor } from "./redaction.js";
8
- import type { QaSessionLogReader } from "./session-log.js";
8
+ import type { QaSessionLogReader, QaStoredSessionEraser } from "./session-log.js";
9
9
  /**
10
10
  * The administrative console's server side.
11
11
  *
@@ -31,6 +31,14 @@ export interface QaAdminServiceOptions {
31
31
  readonly roles: () => QaRoleRepository;
32
32
  readonly access: () => QaAccessService;
33
33
  readonly sessionLog: QaSessionLogReader;
34
+ /**
35
+ * Removal of stored session logs, the one thing the Harness does not offer.
36
+ * Without it the console refuses to delete a conversation rather than
37
+ * pretending it did.
38
+ */
39
+ readonly sessionFiles?: QaStoredSessionEraser;
40
+ /** Drops the sources a conversation collected, when the deployment keeps any. */
41
+ readonly dropSources?: (sessionId: string) => void;
34
42
  readonly logger: PluginLogger;
35
43
  readonly redactor?: QaAdminRedactor;
36
44
  readonly clock?: QaServiceClock;
@@ -52,6 +60,12 @@ export declare class QaAdminService {
52
60
  * enriched with the stored header when the deployment can list one. A chat
53
61
  * that exists but has no readable log still appears — the reviewer must see
54
62
  * that it happened.
63
+ *
64
+ * Reading the index first asks the access sweep to reclaim what the Harness
65
+ * no longer knows: a chat deleted outside the deployment would otherwise sit
66
+ * in this list, in the counters and in the metrics until some later chat
67
+ * creation happened to trigger housekeeping. The sweep is throttled, so this
68
+ * costs a listing per interval at most.
55
69
  */
56
70
  private index;
57
71
  /** One conversation's projected log, cached for the TTL unless asked fresh. */
@@ -76,6 +90,24 @@ export declare class QaAdminService {
76
90
  conversations(token: string, query: QaConversationQuery, cursor: string | undefined, limit: number | undefined): Promise<QaAdminPage<QaConversationSummary>>;
77
91
  private summaryOf;
78
92
  conversation(token: string, conversationId: string): Promise<QaConversationDetail>;
93
+ /**
94
+ * Delete one conversation from the deployment.
95
+ *
96
+ * The Harness has no deletion seam: persistence offers create, open, flush,
97
+ * stat and list, and a chat stops existing only when its stored log is gone.
98
+ * This is where the console's delete button gets its teeth — the stored logs
99
+ * of the conversation and of every session delegated from it are removed,
100
+ * and then everything the QA layer kept about it: the ownership record that
101
+ * is its authorization boundary, its ratings, reviews and queue entries, and
102
+ * the sources it collected. The act is audited, because it destroys what a
103
+ * person wrote, and that is not the same as hiding a row.
104
+ *
105
+ * Every refusal comes before anything is removed, so a chat is never
106
+ * half-deleted: a conversation the Harness still holds open would have its
107
+ * log written back by the next flush, and one the deployment stores somewhere
108
+ * directories do not express cannot be removed at all.
109
+ */
110
+ deleteConversation(token: string, conversationId: string): Promise<QaConversationDeletion>;
79
111
  private failuresOf;
80
112
  private failureSignals;
81
113
  /**
@@ -35,9 +35,54 @@ export type QaSessionReadResult = {
35
35
  readonly ok: false;
36
36
  readonly reason: QaTranscriptUnavailableReason;
37
37
  };
38
+ /**
39
+ * One listing of the deployment's sessions together with what the answer
40
+ * covers. The distinction is load-bearing: on a deployment that serves no
41
+ * durable query engine the listing is only what this process has open, and a
42
+ * chat nobody opened since the last restart is absent from it without being
43
+ * gone.
44
+ */
45
+ export interface QaSessionListing {
46
+ readonly headers: readonly QaStoredSessionHeader[];
47
+ /**
48
+ * True when the listing answers for every stored session, not only the live
49
+ * ones. A consumer that must never read "absent" as "deleted" — the
50
+ * ownership sweep — reclaims nothing against an incomplete listing.
51
+ */
52
+ readonly complete: boolean;
53
+ }
38
54
  export interface QaSessionLogReader {
39
- list(): Promise<readonly QaStoredSessionHeader[]>;
55
+ list(): Promise<QaSessionListing>;
40
56
  read(sessionId: string): Promise<QaSessionReadResult>;
57
+ /**
58
+ * Whether the Harness holds this session in memory right now. Such a session
59
+ * is still being written: storage removal cannot take it away, because the
60
+ * next flush would put its log back.
61
+ */
62
+ live(sessionId: string): boolean;
63
+ }
64
+ /** What one storage-removal call did, id by id. */
65
+ export interface QaSessionEraseOutcome {
66
+ /** Ids whose stored directories were removed. */
67
+ readonly removed: readonly string[];
68
+ /**
69
+ * Ids with no stored directory to remove: the session is already gone, or
70
+ * this deployment keeps it somewhere directories cannot express.
71
+ */
72
+ readonly absent: readonly string[];
73
+ }
74
+ /**
75
+ * Removal of stored sessions.
76
+ *
77
+ * The Harness has no deletion seam: `sessionPersistence` offers
78
+ * create/open/flush/stat/list, and the in-memory store drops a session only
79
+ * when the fiber that owns it disposes. A deployment that wants a conversation
80
+ * to stop existing therefore has to remove the artifacts storage keeps, which
81
+ * for the directory-backed backend means the session directories under the
82
+ * home. Implementations report what they could not do instead of guessing.
83
+ */
84
+ export interface QaStoredSessionEraser {
85
+ erase(sessionIds: readonly string[]): Promise<QaSessionEraseOutcome>;
41
86
  }
42
87
  /**
43
88
  * Build the reader for one plugin instance. The services are looked up per
@@ -49,5 +94,24 @@ export declare function createSessionLogReader(ctx: Context): QaSessionLogReader
49
94
  export declare function staticSessionLogReader(input: {
50
95
  readonly sessions?: readonly QaStoredSessionHeader[];
51
96
  readonly events?: Readonly<Record<string, readonly StoredSessionEvent[]>>;
97
+ /**
98
+ * Whether the listed sessions are the fixture's whole world. True by
99
+ * default — a fixture that names its sessions means them to be all of them;
100
+ * pass false to stand for a live-only view of a larger deployment.
101
+ */
102
+ readonly complete?: boolean;
103
+ /** Ids the fixture's Harness still holds open. */
104
+ readonly held?: readonly string[];
52
105
  }): QaSessionLogReader;
106
+ /**
107
+ * The stored sessions of a directory-backed deployment: one directory per
108
+ * session under `<home>/sessions/<project>/<sessionId>`, named by the id the
109
+ * header carries. The home follows the same convention as the deployment's
110
+ * other stores (`DSH_HOME`, then the working directory).
111
+ *
112
+ * Removal is by directory name and never by guessing a project key: the id is
113
+ * what the caller knows, and the directory either exists under one of the
114
+ * projects or does not exist at all.
115
+ */
116
+ export declare function createSessionEraser(home?: string): QaStoredSessionEraser;
53
117
  //# sourceMappingURL=session-log.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import type { ConnectionGenerationState } from "@deepseek-ai/dsh-client-connection/client";
2
2
  import type { QaAttachmentDraft, QaSessionState, ResolvedQaSurfaceConfig } from "../types.js";
3
- import type { QaApprovalApi, QaConversation, QaCreateSession, QaFileUpload, QaSecureSession, QaSessions, QaSessionsApi, QaQuestionApi, QaSourceApi, StorageLike } from "./types.js";
3
+ import type { QaApprovalApi, QaConversation, QaCreateSession, QaFileUpload, QaSecureSession, QaSessions, QaSessionsApi, QaQuestionApi, QaSlashApi, QaSourceApi, StorageLike } from "./types.js";
4
4
  import type { QaApprovalDecision, QaQuestionAnswerItem } from "../types.js";
5
5
  export interface QaAccountsFacade {
6
6
  /** The account bearer token, or null while anonymous. */
@@ -38,6 +38,12 @@ export interface QaSessionControllerOptions {
38
38
  * does not answer questions: the surface then only renders what it can.
39
39
  */
40
40
  readonly questionApi?: QaQuestionApi;
41
+ /**
42
+ * The slash half of the Host namespace. Absent on a page whose Host build
43
+ * predates it: the palette then never appears and ordinary prompts keep
44
+ * working, which is exactly the pre-feature behaviour.
45
+ */
46
+ readonly slashApi?: QaSlashApi;
41
47
  readonly storage?: StorageLike;
42
48
  /**
43
49
  * Browser file-upload service, resolved lazily: the page may not serve the
@@ -74,6 +80,8 @@ export declare class QaSessionController {
74
80
  private readonly sourceApi;
75
81
  private readonly chats;
76
82
  private readonly accounts;
83
+ /** Slash half of the Host namespace; absent on an older Host build. */
84
+ private readonly slashApi;
77
85
  /** Resolved per send: a page without the upload plugin has no receipts. */
78
86
  private readonly fileUpload;
79
87
  /** Per-chat attachment URL cache; blob URLs die with the chat binding. */
@@ -114,6 +122,25 @@ export declare class QaSessionController {
114
122
  private readonly hostQuestions;
115
123
  /** Set while a running turn is polled for parked requests. */
116
124
  private pendingTimer;
125
+ /**
126
+ * Slash catalog of the bound chat. Keyed by session: it depends on the
127
+ * chat's cwd, its agent composition and its role, so it is dropped with the
128
+ * binding instead of being carried across chats.
129
+ */
130
+ private slashSessionId;
131
+ private slashEntries;
132
+ private slashDenied;
133
+ private slashSurface;
134
+ private slashState;
135
+ private slashError;
136
+ private slashLoadedAt;
137
+ private slashLoading;
138
+ /** Last published view; its identity is the render signal. */
139
+ private slashViewCache;
140
+ /** Bumped to re-open a palette the user dismissed; see `QaSlashView`. */
141
+ private slashReopen;
142
+ /** Chat, connection and admission state of the last one-shot probe. */
143
+ private pendingProbeKey;
117
144
  constructor(options: QaSessionControllerOptions);
118
145
  getSnapshot: () => QaSessionState;
119
146
  subscribe: (listener: () => void) => (() => void);
@@ -124,7 +151,48 @@ export declare class QaSessionController {
124
151
  * favor of a fresh Host-owned session.
125
152
  */
126
153
  selectSubrole(subroleId: string, adminPreview?: boolean): Promise<void>;
127
- send(text: string, attachments?: readonly QaAttachmentDraft[]): Promise<boolean>;
154
+ /**
155
+ * The attested live session a submission may ride, or `undefined` when the
156
+ * caller must abort. Materializing a draft chat, attesting policy and
157
+ * recovering from a stale proof live here, so the prompt path and the
158
+ * command path cannot drift apart on what "ready to send" means.
159
+ */
160
+ private liveTarget;
161
+ /**
162
+ * Route one composer draft. Ordinary text and a skill invocation both ride
163
+ * the native prompt path — a skill is a gesture inside the prompt, not a
164
+ * call this surface makes — while a human command goes to the Host's command
165
+ * runtime and never becomes a model message.
166
+ */
167
+ send(text: string, attachments?: readonly QaAttachmentDraft[], pick?: string | null): Promise<boolean>;
168
+ /**
169
+ * Run one admitted human command. The Host answers it and writes the
170
+ * lifecycle into the session log; this method only decides what the composer
171
+ * does next, which is why nothing here creates a pending user row.
172
+ *
173
+ * A refusal keeps the draft and the attachments: the user asked for
174
+ * something the deployment does not allow, and retyping the line is a worse
175
+ * answer than leaving it in place to be edited.
176
+ */
177
+ private sendCommand;
178
+ /**
179
+ * Refresh the catalog of the bound chat. Called when a chat binds, and again
180
+ * when the palette opens on a catalog that has had time to go stale: the
181
+ * skill registry has no browser-facing change event, so a skill installed
182
+ * while the page is open is only discovered by asking again.
183
+ */
184
+ refreshSlashCatalog(force?: boolean): Promise<void>;
185
+ /**
186
+ * Install one catalog answer. `null` means the deployment runs with the
187
+ * slash interface off; `undefined` means the answer never arrived. The
188
+ * difference matters: an unreachable catalog must not turn ordinary prompts
189
+ * into refusals.
190
+ */
191
+ private applySlashCatalog;
192
+ /** Whether the catalog of the bound chat was read successfully. */
193
+ private slashCatalogReady;
194
+ /** The slash view of the current state; assembled where the state is. */
195
+ private slashView;
128
196
  /** Publish a browser-only copy before any network or Host admission awaits. */
129
197
  private beginSubmission;
130
198
  /**
@@ -239,8 +307,22 @@ export declare class QaSessionController {
239
307
  * inside an open turn, and it is Host state: polling is how the page learns
240
308
  * about it, and how it reappears after a reload. One timer serves both seams,
241
309
  * and each is polled only where the deployment answers it.
310
+ *
311
+ * A request that is already on screen keeps the timer alive past the end of
312
+ * its turn: the Host settles what the turn can no longer answer — an aborted
313
+ * tool call, an agent that went idle — and this keeps reading until that
314
+ * settlement reaches the page, so the form goes away instead of sitting
315
+ * there answerable but dead.
242
316
  */
243
317
  private syncPendingPolling;
318
+ /**
319
+ * Read what the Host parks once per chat and connection, whether or not a
320
+ * turn is running. A reload binds the chat before any turn reports itself
321
+ * over the fresh stream, and a turn that is already waiting on a question
322
+ * would otherwise sit behind an empty composer until the operator happened
323
+ * to send something.
324
+ */
325
+ private probePending;
244
326
  private stopPendingPolling;
245
327
  private hostToken;
246
328
  }
@@ -3,9 +3,10 @@ import type { InjectFace, PropsRenderSlots, PropsRuntime } from "@deepseek-ai/ds
3
3
  import type { QaConfigController } from "./QaConfigController.js";
4
4
  import type { QaRouteController } from "./QaRouteController.js";
5
5
  import type { QaAccountsController } from "./QaAccountsController.js";
6
- import type { QaApprovalApi, QaAccessApi, QaAdminApi, QaConversation, QaCreateSession, QaFileUpload, QaSecureSession, QaSessions, QaSessionsApi, QaQuestionApi, QaSkillApi, QaSourceApi } from "./types.js";
6
+ import type { QaApprovalApi, QaAccessApi, QaAdminApi, QaConversation, QaCreateSession, QaFileUpload, QaSecureSession, QaSessions, QaSessionsApi, QaQuestionApi, QaSkillApi, QaSlashApi, QaSourceApi } from "./types.js";
7
7
  import type { QaSurfacePanelRegistry } from "./panels/registry.js";
8
8
  import type { QaUserSettingsSections } from "./settings-extensions/index.js";
9
+ import type { QaAuditController } from "./audit/controller.js";
9
10
  export interface QaSurfaceFace {
10
11
  readonly route: QaRouteController;
11
12
  readonly config: QaConfigController;
@@ -34,6 +35,12 @@ export interface QaSurfaceFace {
34
35
  readonly approvalApi?: QaApprovalApi;
35
36
  /** Question half of the plugin's namespace, when the Host answers it. */
36
37
  readonly questionApi?: QaQuestionApi;
38
+ /**
39
+ * Slash half of the plugin's namespace. Absent on a Host build that predates
40
+ * it: the composer then never opens a palette and ordinary prompts are
41
+ * untouched.
42
+ */
43
+ readonly slashApi?: QaSlashApi;
37
44
  /**
38
45
  * Browser file-upload service, when the page serves the upload plugin.
39
46
  * Resolved per send so a page that loads it later still gets file support.
@@ -45,6 +52,12 @@ export interface QaSurfaceFace {
45
52
  readonly panels: QaSurfacePanelRegistry;
46
53
  /** First-class settings pages registered by additive QA plugins. */
47
54
  readonly settingsSections: QaUserSettingsSections;
55
+ /**
56
+ * Whether the session-audit provider is installed. Its snapshot carries the
57
+ * audit API, or `null` — the badge and the dialog exist only while it is
58
+ * present.
59
+ */
60
+ readonly audit: QaAuditController;
48
61
  }
49
62
  export type QaSurfaceProps = PropsRuntime<"shell.overlay"> & PropsRenderSlots<"qa.surface.panel"> & InjectFace<QaSurfaceFace>;
50
63
  export declare function QaSurface(props: QaSurfaceProps): import("react/jsx-runtime").JSX.Element | null;
@@ -40,4 +40,4 @@ export declare function formatRelative(iso: string, now?: number): string;
40
40
  /** Milliseconds as a short duration; tool timings are second-scale. */
41
41
  export declare function formatDuration(ms: number | undefined): string;
42
42
  export declare function formatCount(value: number): string;
43
- //# sourceMappingURL=format.d.ts.map
43
+ //# sourceMappingURL=copy.d.ts.map
@@ -16,6 +16,10 @@ export declare function AdminConversation(props: {
16
16
  readonly conversationId: string;
17
17
  readonly messageId?: string;
18
18
  readonly canReview: boolean;
19
+ /** Removing the conversation itself belongs to the administrator alone. */
20
+ readonly canDelete: boolean;
19
21
  readonly onBack: () => void;
22
+ /** Called once the conversation is gone, so the console leaves the page. */
23
+ readonly onDeleted?: () => void;
20
24
  }): import("react/jsx-runtime").JSX.Element;
21
25
  //# sourceMappingURL=Conversations.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The audit dialog: the same content as the session page, in this surface's
3
+ * own shell (SPEC §51).
4
+ *
5
+ * The status bar is compact and the tab strip is shared, because the audit is
6
+ * the same audit — a reader who has seen it in the ordinary DSH session should
7
+ * recognise every number here.
8
+ */
9
+ import { type ReactNode } from "react";
10
+ import type { QaAuditApi, QaAuditSummary } from "./types.js";
11
+ export interface QaAuditDialogProps {
12
+ readonly open: boolean;
13
+ readonly sessionId: string | null;
14
+ readonly title: string;
15
+ readonly api: QaAuditApi | null;
16
+ /** The summary the badge was built from, for the reload key. */
17
+ readonly summary: QaAuditSummary | undefined;
18
+ readonly onClose: () => void;
19
+ }
20
+ export declare function QaAuditDialog(props: QaAuditDialogProps): ReactNode;
21
+ //# sourceMappingURL=QaAuditDialog.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The row badge: "this chat was audited", and what the audit found.
3
+ *
4
+ * Two visually distinct things, on purpose (SPEC §52). The check mark and the
5
+ * label say *an audit exists*; the verdict and the counts say *what it found*.
6
+ * A green tick next to "poor" has to read as "audited, and it went badly",
7
+ * which is why the tick is never the thing that carries the quality.
8
+ */
9
+ import type { ReactNode } from "react";
10
+ import type { QaAuditMark } from "./types.js";
11
+ export interface QaAuditRowBadgeProps {
12
+ readonly mark: QaAuditMark;
13
+ /** Whether the row also carries a delete control, which takes the right slot. */
14
+ readonly withDelete: boolean;
15
+ readonly onOpen: () => void;
16
+ }
17
+ export declare function QaAuditRowBadge(props: QaAuditRowBadgeProps): ReactNode;
18
+ //# sourceMappingURL=QaAuditRowBadge.d.ts.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Whether the audit provider is present, as a client-side service.
3
+ *
4
+ * The audit plugin may or may not be installed, and its Remote namespace
5
+ * arrives asynchronously. A React tree cannot watch a Cordis injection by
6
+ * itself, so the attach/detach pair below is exposed as an external store and
7
+ * the surface reads it with `useSyncExternalStore` — the same shape the rest
8
+ * of this client uses for host-driven state.
9
+ */
10
+ import type { QaAuditApi } from "./types.js";
11
+ /** The snapshot the surface renders from; replaced, never mutated. */
12
+ export interface QaAuditSnapshot {
13
+ readonly api: QaAuditApi | null;
14
+ }
15
+ export declare class QaAuditController {
16
+ private snapshot;
17
+ private readonly listeners;
18
+ /** The provider's namespace resolved: audits become visible. */
19
+ attach(api: QaAuditApi): void;
20
+ /** The provider went away: the badge and the dialog disappear with it. */
21
+ detach(): void;
22
+ getSnapshot: () => QaAuditSnapshot;
23
+ subscribe: (listener: () => void) => (() => void);
24
+ dispose(): void;
25
+ private emit;
26
+ }
27
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The QA Surface's view of the session-audit Remote namespace.
3
+ *
4
+ * Declared here, structurally, rather than imported from `@yadsh/dsh-session-audit`:
5
+ * a plugin may not depend on another plugin, and it does not need to. The
6
+ * namespace is a wire contract, and the surface simply asks whether it is
7
+ * there — when the audit plugin is not installed, `ctx.inject` never fires and
8
+ * the badge and the dialog are simply absent (SPEC §48).
9
+ */
10
+ /** The cheap per-session summary the badge reads. */
11
+ export interface QaAuditSummary {
12
+ readonly available: boolean;
13
+ readonly auditId: string;
14
+ readonly sessionId: string;
15
+ readonly verdict: string;
16
+ readonly outcomeStatus: string;
17
+ readonly evidenceLevel: string;
18
+ readonly model: string;
19
+ readonly agentPreset: string;
20
+ readonly toolCalls: number;
21
+ readonly toolErrors: number;
22
+ readonly critical: number;
23
+ readonly major: number;
24
+ readonly minor: number;
25
+ readonly observation: number;
26
+ readonly other: number;
27
+ readonly schemaVersion: number;
28
+ readonly modifiedAt: string;
29
+ }
30
+ /** The full audit, as two documents and their summary. */
31
+ export interface QaSessionAudit {
32
+ readonly available: boolean;
33
+ readonly summary: QaAuditSummary;
34
+ readonly analysisJson: string;
35
+ readonly report: string;
36
+ }
37
+ /** A failed Remote result, narrowed to what the surface reports. */
38
+ interface RemoteFailure {
39
+ readonly ok: false;
40
+ readonly error: {
41
+ readonly code: string;
42
+ };
43
+ }
44
+ type RemoteResult<T> = {
45
+ readonly ok: true;
46
+ readonly value: T;
47
+ } | RemoteFailure;
48
+ /** The `sessionAudit` namespace as the browser sees it. */
49
+ export interface QaAuditRemote {
50
+ summary(sessionId: string): Promise<RemoteResult<QaAuditSummary>>;
51
+ audit(sessionId: string): Promise<RemoteResult<QaSessionAudit>>;
52
+ }
53
+ /** The narrow surface the surface's components are handed. */
54
+ export interface QaAuditApi {
55
+ summary(sessionId: string): Promise<QaAuditSummary>;
56
+ audit(sessionId: string): Promise<QaSessionAudit>;
57
+ }
58
+ /** Build the facade over a mounted Remote namespace. */
59
+ export declare function createQaAuditApi(remote: QaAuditRemote): QaAuditApi;
60
+ /** The numbers the row badge shows, or `null` when there is no audit. */
61
+ export interface QaAuditMark {
62
+ readonly verdict: string;
63
+ readonly critical: number;
64
+ readonly major: number;
65
+ readonly minor: number;
66
+ readonly observation: number;
67
+ readonly other: number;
68
+ }
69
+ /** Reduce a summary to the mark a row shows. */
70
+ export declare function auditMark(summary: QaAuditSummary | undefined): QaAuditMark | null;
71
+ export {};
72
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,20 @@
1
+ import type { QaAuditApi, QaSessionAudit, QaAuditSummary } from "./types.js";
2
+ /** How often the chat list re-asks which chats have an audit. */
3
+ export declare const AUDIT_POLL_MS = 30000;
4
+ /**
5
+ * Poll the audit summary for every chat id.
6
+ *
7
+ * @param api - the Remote facade, or `null` when the audit plugin is absent.
8
+ * @param sessionIds - the chats currently listed.
9
+ */
10
+ export declare function useChatAudits(api: QaAuditApi | null, sessionIds: readonly string[]): ReadonlyMap<string, QaAuditSummary>;
11
+ /** The dialog's own load: one audit's documents, fetched when it opens. */
12
+ export interface QaAuditLoad {
13
+ readonly loading: boolean;
14
+ readonly audit: QaSessionAudit | null;
15
+ readonly error: string | null;
16
+ }
17
+ export declare function useSessionAuditDocument(api: QaAuditApi | null, sessionId: string | null, open: boolean,
18
+ /** Reload key; the summary's `modifiedAt` changes exactly when bytes do. */
19
+ modifiedAt: string | null): QaAuditLoad;
20
+ //# sourceMappingURL=use-chat-audits.d.ts.map
@@ -1,13 +1,12 @@
1
1
  import type { QaSource } from "../../types.js";
2
- import { type QaSourceRefs } from "./source-refs.js";
3
- /** Inline source awareness handed down from the owning assistant message. */
4
- export interface MarkdownSourceContext {
5
- readonly refs?: QaSourceRefs;
6
- /** Open a path-backed source in the sources drawer's detail view. */
7
- readonly onSourceOpen?: (source: QaSource) => void;
8
- }
2
+ import type { QaSourceRefs } from "./source-refs.js";
3
+ export type { MarkdownSourceContext } from "../markdown/source-chip.js";
9
4
  /**
10
- * Deliberately small, HTML-free Markdown renderer for assistant-visible text.
5
+ * Assistant-visible Markdown. The grammar lives in `../markdown/`: blocks are
6
+ * parsed once per text value, inline content is resolved per block, and a link
7
+ * or inline-code token that names a source the owning message knows about
8
+ * renders as a source chip instead of plain text.
9
+ *
11
10
  * Memoized on the text value: stream updates re-render only the message whose
12
11
  * text actually changed, so committed history is never re-parsed.
13
12
  */
@@ -1,4 +1,4 @@
1
- import type { QaAttachmentDraft } from "../../types.js";
1
+ import type { QaAttachmentDraft, QaSlashView, ResolvedQaSlashCommands } from "../../types.js";
2
2
  import { type QaAttachmentLimits } from "../attachments.js";
3
3
  import type { QaQuickQuestion } from "../types.js";
4
4
  export interface QaComposerProps {
@@ -13,8 +13,18 @@ export interface QaComposerProps {
13
13
  readonly attachments: readonly QaAttachmentDraft[];
14
14
  /** Deployment policy: accepted kinds, ceilings and the paste threshold. */
15
15
  readonly limits: QaAttachmentLimits;
16
+ /** Slash catalog of this chat; inert while the master switch is off. */
17
+ readonly slash: QaSlashView;
18
+ readonly slashPolicy: ResolvedQaSlashCommands["palette"];
16
19
  readonly onAttachmentsChange: (attachments: readonly QaAttachmentDraft[]) => void;
17
- readonly onSend: (text: string, attachments: readonly QaAttachmentDraft[]) => Promise<boolean>;
20
+ /**
21
+ * `pick` names the entry the user chose in the palette, while the draft
22
+ * still reads as its invocation. It is what disambiguates a skill and a
23
+ * command that share a name; `null` means the draft alone decides.
24
+ */
25
+ readonly onSend: (text: string, attachments: readonly QaAttachmentDraft[], pick: string | null) => Promise<boolean>;
26
+ /** Called when the palette opens, so the caller can revalidate the catalog. */
27
+ readonly onSlashOpen: () => void;
18
28
  readonly onStop: () => Promise<void>;
19
29
  }
20
30
  /**
@@ -3,6 +3,13 @@ export interface QaQuestionsProps {
3
3
  readonly questions: readonly QaPendingQuestion[];
4
4
  readonly onAnswer: (requestId: string, answers: readonly QaQuestionAnswerItem[]) => Promise<void>;
5
5
  readonly onCancel: (requestId: string) => Promise<void>;
6
+ /**
7
+ * Stop the turn the questions belong to. The form takes the composer's
8
+ * place, so the run's own stop action moves here: an operator who does not
9
+ * want to answer must still be able to end the turn instead.
10
+ */
11
+ readonly onStop?: () => Promise<void>;
12
+ readonly canStop?: boolean;
6
13
  }
7
14
  /**
8
15
  * The questions a running turn waits on. Answers are the operator's own: the
@@ -1,4 +1,5 @@
1
1
  import type { SessionSummary } from "@deepseek-ai/dsh-api-session-controller/client";
2
+ import { type QaAuditSummary } from "../audit/types.js";
2
3
  /** One renderable row of the chat-history sidebar. */
3
4
  export interface QaChatRow {
4
5
  readonly id: string;
@@ -14,7 +15,9 @@ export interface QaChatRow {
14
15
  * Project this browser's indexed chat ids onto the host session list, most
15
16
  * recently updated first. Opening a chat is not an update: only the host's
16
17
  * `updatedAt` (fresh messages) orders the list. Ids the host no longer lists
17
- * are skipped (the controller prunes them on switch).
18
+ * are skipped (the controller prunes them on switch), and so is a delegated
19
+ * child: a subagent's session is not a chat, even when an ownership record
20
+ * from an older release still names it.
18
21
  */
19
22
  export declare function buildChatRows(chatIds: readonly string[], byId: Readonly<Record<string, SessionSummary>>, activeId: string | null, ownerNameOf?: (sessionId: string) => string | undefined, now?: number): readonly QaChatRow[];
20
23
  /** One per-owner section of the admin sidebar grouping. */
@@ -51,6 +54,13 @@ export interface QaSidebarProps {
51
54
  readonly onNewChat: () => void;
52
55
  /** Removes a chat from this browser's index; omit to hide the control. */
53
56
  readonly onDelete?: (sessionId: string) => void;
57
+ /**
58
+ * Audit summaries by chat id, for the row badge. Empty when the audit plugin
59
+ * is not installed — the badge is then absent rather than empty.
60
+ */
61
+ readonly audits?: ReadonlyMap<string, QaAuditSummary>;
62
+ /** Opens the audit dialog for a chat; omit when audits are unavailable. */
63
+ readonly onAudit?: (sessionId: string) => void;
54
64
  /** The signed-in account; omit when accounts are disabled. */
55
65
  readonly account?: {
56
66
  readonly email: string;