@yadsh/dsh-qa-surface 0.9.0 → 0.11.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 (250) hide show
  1. package/README.md +269 -2
  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 +180 -7
  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 +49 -21
  30. package/lib/client/QaSessionController.js.map +1 -1
  31. package/lib/client/QaSurface.js +51 -9
  32. package/lib/client/QaSurface.js.map +1 -1
  33. package/lib/client/QaSurfaceGuard.js +12 -5
  34. package/lib/client/QaSurfaceGuard.js.map +1 -1
  35. package/lib/client/admin/QaAdmin.js +14 -2
  36. package/lib/client/admin/QaAdmin.js.map +1 -1
  37. package/lib/client/admin/pages/SkillFiles.js +78 -0
  38. package/lib/client/admin/pages/SkillFiles.js.map +1 -0
  39. package/lib/client/admin/pages/Users.js +74 -3
  40. package/lib/client/admin/pages/Users.js.map +1 -1
  41. package/lib/client/admin/routes.js +8 -1
  42. package/lib/client/admin/routes.js.map +1 -1
  43. package/lib/client/admin/shared.js +5 -1
  44. package/lib/client/admin/shared.js.map +1 -1
  45. package/lib/client/admin/styles.js +2 -0
  46. package/lib/client/admin/styles.js.map +1 -1
  47. package/lib/client/attachments.js +41 -11
  48. package/lib/client/attachments.js.map +1 -1
  49. package/lib/client/base64.js +22 -1
  50. package/lib/client/base64.js.map +1 -1
  51. package/lib/client/components/QaAgentsDrawer.js +9 -6
  52. package/lib/client/components/QaAgentsDrawer.js.map +1 -1
  53. package/lib/client/components/QaApproval.js +1 -1
  54. package/lib/client/components/QaApproval.js.map +1 -1
  55. package/lib/client/components/QaAuthGate.js +19 -10
  56. package/lib/client/components/QaAuthGate.js.map +1 -1
  57. package/lib/client/components/QaChangelog.js +56 -0
  58. package/lib/client/components/QaChangelog.js.map +1 -1
  59. package/lib/client/components/QaFilesPanel.js +17 -11
  60. package/lib/client/components/QaFilesPanel.js.map +1 -1
  61. package/lib/client/components/QaHeader.js +3 -11
  62. package/lib/client/components/QaHeader.js.map +1 -1
  63. package/lib/client/components/QaModal.js.map +1 -1
  64. package/lib/client/components/QaQuestions.js +4 -4
  65. package/lib/client/components/QaQuestions.js.map +1 -1
  66. package/lib/client/components/QaSidebar.js +10 -2
  67. package/lib/client/components/QaSidebar.js.map +1 -1
  68. package/lib/client/components/QaWidthHandle.js +111 -5
  69. package/lib/client/components/QaWidthHandle.js.map +1 -1
  70. package/lib/client/components/QaWorkspaceBrowser.js +231 -0
  71. package/lib/client/components/QaWorkspaceBrowser.js.map +1 -0
  72. package/lib/client/index.js +122 -53
  73. package/lib/client/index.js.map +1 -1
  74. package/lib/client/kiosk.js +51 -0
  75. package/lib/client/kiosk.js.map +1 -0
  76. package/lib/client/lineage.js +64 -0
  77. package/lib/client/lineage.js.map +1 -1
  78. package/lib/client/markdown/blocks.js +122 -7
  79. package/lib/client/markdown/blocks.js.map +1 -1
  80. package/lib/client/markdown/inline.js +108 -12
  81. package/lib/client/markdown/inline.js.map +1 -1
  82. package/lib/client/markdown/katex-css.js +12 -0
  83. package/lib/client/markdown/katex-css.js.map +1 -0
  84. package/lib/client/markdown/math.js +84 -0
  85. package/lib/client/markdown/math.js.map +1 -0
  86. package/lib/client/markdown/render.js +74 -5
  87. package/lib/client/markdown/render.js.map +1 -1
  88. package/lib/client/settings/card.js +2 -2
  89. package/lib/client/settings/card.js.map +1 -1
  90. package/lib/client/settings/sections/attachments.js +6 -5
  91. package/lib/client/settings/sections/attachments.js.map +1 -1
  92. package/lib/client/settings/sections/notes.js +64 -0
  93. package/lib/client/settings/sections/notes.js.map +1 -0
  94. package/lib/client/settings/sections/sources.js +4 -1
  95. package/lib/client/settings/sections/sources.js.map +1 -1
  96. package/lib/client/settings/sections.js +1 -0
  97. package/lib/client/settings/sections.js.map +1 -1
  98. package/lib/client/settlement.js +1 -1
  99. package/lib/client/settlement.js.map +1 -1
  100. package/lib/client/source-preview.js +3 -0
  101. package/lib/client/source-preview.js.map +1 -1
  102. package/lib/client/styles.js +106 -18
  103. package/lib/client/styles.js.map +1 -1
  104. package/lib/client/types.js.map +1 -1
  105. package/lib/client/user-settings/IntegrationTokensPage.js +150 -0
  106. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -0
  107. package/lib/client/user-settings/PasswordSettingsPage.js +48 -0
  108. package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -0
  109. package/lib/client/user-settings/SkillCatalog.js +2 -2
  110. package/lib/client/user-settings/SkillCatalog.js.map +1 -1
  111. package/lib/client/user-settings/UserSettingsDialog.js +17 -2
  112. package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
  113. package/lib/client/user-settings/copy.js +20 -0
  114. package/lib/client/user-settings/copy.js.map +1 -1
  115. package/lib/client.js +31007 -686
  116. package/lib/client.js.map +1 -1
  117. package/lib/config-resolvers/attachments.js +6 -5
  118. package/lib/config-resolvers/attachments.js.map +1 -1
  119. package/lib/config-resolvers/defaults.js +33 -2
  120. package/lib/config-resolvers/defaults.js.map +1 -1
  121. package/lib/config-resolvers/integration.js +94 -0
  122. package/lib/config-resolvers/integration.js.map +1 -0
  123. package/lib/config-resolvers/notes.js +26 -0
  124. package/lib/config-resolvers/notes.js.map +1 -0
  125. package/lib/config-resolvers/sources.js +4 -0
  126. package/lib/config-resolvers/sources.js.map +1 -1
  127. package/lib/config.js +107 -0
  128. package/lib/config.js.map +1 -1
  129. package/lib/index.js +348 -12
  130. package/lib/index.js.map +1 -1
  131. package/lib/integration/answer.js +100 -0
  132. package/lib/integration/answer.js.map +1 -0
  133. package/lib/integration/attachments.js +231 -0
  134. package/lib/integration/attachments.js.map +1 -0
  135. package/lib/integration/contract.js +53 -0
  136. package/lib/integration/contract.js.map +1 -0
  137. package/lib/integration/host-runner.js +243 -0
  138. package/lib/integration/host-runner.js.map +1 -0
  139. package/lib/integration/http.js +538 -0
  140. package/lib/integration/http.js.map +1 -0
  141. package/lib/integration/service.js +331 -0
  142. package/lib/integration/service.js.map +1 -0
  143. package/lib/integration/transcript-reader.js +202 -0
  144. package/lib/integration/transcript-reader.js.map +1 -0
  145. package/lib/integration/transcript.js +148 -0
  146. package/lib/integration/transcript.js.map +1 -0
  147. package/lib/personal-skills/index.js +2 -2
  148. package/lib/personal-skills/index.js.map +1 -1
  149. package/lib/personal-skills/paths.js +14 -8
  150. package/lib/personal-skills/paths.js.map +1 -1
  151. package/lib/personal-skills/provider.js +16 -4
  152. package/lib/personal-skills/provider.js.map +1 -1
  153. package/lib/personal-skills/service.js +240 -30
  154. package/lib/personal-skills/service.js.map +1 -1
  155. package/lib/prompt-notes.js +103 -6
  156. package/lib/prompt-notes.js.map +1 -1
  157. package/lib/provenance/document-preview.js +60 -0
  158. package/lib/provenance/document-preview.js.map +1 -0
  159. package/lib/provenance/file-preview.js +191 -19
  160. package/lib/provenance/file-preview.js.map +1 -1
  161. package/lib/qa-tools/catalog.js +18 -1
  162. package/lib/qa-tools/catalog.js.map +1 -1
  163. package/lib/qa-tools/docs-tools.js +563 -0
  164. package/lib/qa-tools/docs-tools.js.map +1 -0
  165. package/lib/qa-tools/file-delete-gate.js +60 -0
  166. package/lib/qa-tools/file-delete-gate.js.map +1 -0
  167. package/lib/qa-tools/file-delete.js +156 -0
  168. package/lib/qa-tools/file-delete.js.map +1 -0
  169. package/lib/resolve-config.js +6 -0
  170. package/lib/resolve-config.js.map +1 -1
  171. package/lib/secure-session.js +83 -3
  172. package/lib/secure-session.js.map +1 -1
  173. package/lib/shared/documents.js +22 -0
  174. package/lib/shared/documents.js.map +1 -0
  175. package/lib/shared/integration-tokens.js +23 -0
  176. package/lib/shared/integration-tokens.js.map +1 -0
  177. package/lib/typert.host.js +1320 -134
  178. package/lib/typert.remote-client.d.ts +33 -1
  179. package/lib/typert.remote-client.js +1102 -74
  180. package/lib/types/access/service.d.ts +33 -2
  181. package/lib/types/account-remotes.d.ts +30 -1
  182. package/lib/types/accounts/database.d.ts +53 -0
  183. package/lib/types/accounts/errors.d.ts +5 -1
  184. package/lib/types/accounts/service-token.d.ts +70 -0
  185. package/lib/types/accounts/store.d.ts +131 -1
  186. package/lib/types/admin/conversation-log.d.ts +12 -0
  187. package/lib/types/admin/service.d.ts +56 -1
  188. package/lib/types/admin/session-log.d.ts +21 -0
  189. package/lib/types/attachment-rules.d.ts +26 -3
  190. package/lib/types/client/QaAccountsController.d.ts +64 -5
  191. package/lib/types/client/QaSessionController.d.ts +24 -2
  192. package/lib/types/client/admin/pages/SkillFiles.d.ts +17 -0
  193. package/lib/types/client/admin/routes.d.ts +2 -0
  194. package/lib/types/client/admin/shared.d.ts +2 -0
  195. package/lib/types/client/attachments.d.ts +21 -4
  196. package/lib/types/client/base64.d.ts +13 -1
  197. package/lib/types/client/components/QaAgentsDrawer.d.ts +2 -1
  198. package/lib/types/client/components/QaFilesPanel.d.ts +16 -4
  199. package/lib/types/client/components/QaHeader.d.ts +9 -3
  200. package/lib/types/client/components/QaModal.d.ts +4 -3
  201. package/lib/types/client/components/QaWidthHandle.d.ts +48 -2
  202. package/lib/types/client/components/QaWorkspaceBrowser.d.ts +15 -0
  203. package/lib/types/client/index.d.ts +1 -1
  204. package/lib/types/client/kiosk.d.ts +34 -0
  205. package/lib/types/client/lineage.d.ts +42 -0
  206. package/lib/types/client/markdown/blocks.d.ts +14 -1
  207. package/lib/types/client/markdown/inline.d.ts +23 -7
  208. package/lib/types/client/markdown/katex-css.d.ts +11 -0
  209. package/lib/types/client/markdown/math.d.ts +23 -0
  210. package/lib/types/client/markdown/render.d.ts +2 -1
  211. package/lib/types/client/settings/sections/notes.d.ts +12 -0
  212. package/lib/types/client/settings/sections.d.ts +1 -0
  213. package/lib/types/client/settlement.d.ts +4 -1
  214. package/lib/types/client/styles.d.ts +15 -2
  215. package/lib/types/client/types.d.ts +120 -1
  216. package/lib/types/client/user-settings/IntegrationTokensPage.d.ts +21 -0
  217. package/lib/types/client/user-settings/PasswordSettingsPage.d.ts +19 -0
  218. package/lib/types/client/user-settings/UserSettingsDialog.d.ts +11 -2
  219. package/lib/types/client/user-settings/copy.d.ts +10 -1
  220. package/lib/types/config-resolvers/integration.d.ts +47 -0
  221. package/lib/types/config-resolvers/notes.d.ts +10 -0
  222. package/lib/types/config.d.ts +10 -0
  223. package/lib/types/index.d.ts +111 -1
  224. package/lib/types/integration/answer.d.ts +65 -0
  225. package/lib/types/integration/attachments.d.ts +85 -0
  226. package/lib/types/integration/contract.d.ts +196 -0
  227. package/lib/types/integration/host-runner.d.ts +44 -0
  228. package/lib/types/integration/http.d.ts +79 -0
  229. package/lib/types/integration/service.d.ts +120 -0
  230. package/lib/types/integration/transcript-reader.d.ts +100 -0
  231. package/lib/types/integration/transcript.d.ts +59 -0
  232. package/lib/types/personal-skills/index.d.ts +2 -2
  233. package/lib/types/personal-skills/paths.d.ts +15 -3
  234. package/lib/types/personal-skills/provider.d.ts +14 -2
  235. package/lib/types/personal-skills/service.d.ts +89 -14
  236. package/lib/types/prompt-notes.d.ts +37 -0
  237. package/lib/types/provenance/document-preview.d.ts +28 -0
  238. package/lib/types/provenance/file-preview.d.ts +69 -2
  239. package/lib/types/qa-tools/catalog.d.ts +1 -1
  240. package/lib/types/qa-tools/docs-tools.d.ts +160 -0
  241. package/lib/types/qa-tools/file-delete-gate.d.ts +42 -0
  242. package/lib/types/qa-tools/file-delete.d.ts +32 -0
  243. package/lib/types/secure-session.d.ts +52 -1
  244. package/lib/types/shared/documents.d.ts +20 -0
  245. package/lib/types/shared/integration-tokens.d.ts +24 -0
  246. package/lib/types/types.d.ts +263 -3
  247. package/lib/types/ui-mode.d.ts +18 -0
  248. package/lib/ui-mode.js +22 -0
  249. package/lib/ui-mode.js.map +1 -0
  250. package/package.json +12 -10
@@ -0,0 +1,100 @@
1
+ import type { PluginLogger } from "@yadsh/dsh-plugin-log";
2
+ import type { QaSessionLogReader } from "../admin/session-log.js";
3
+ import type { QaIntegrationTranscript } from "./contract.js";
4
+ /**
5
+ * A conversation read warm, for an application that pages through it.
6
+ *
7
+ * The projection itself (`transcript.ts`) answers one page from a log. That is
8
+ * the right shape for a review page opened once and the wrong one for a caller
9
+ * that follows a cursor: every read of a long chat would load the stored log
10
+ * and flatten every message in it to hand back the newest fifty, so a bridge
11
+ * paging through a two-year-old conversation pays for the whole conversation
12
+ * per page, and pays it again on the next poll.
13
+ *
14
+ * What is kept here is the window itself — the newest messages, already
15
+ * flattened — plus the newest seq below it, which is all the arithmetic a page
16
+ * needs to be exact. A page is then either served from that window or, when the
17
+ * window cannot answer for the caller's cursor, read from the log.
18
+ *
19
+ * Two things decide whether the window may be trusted:
20
+ *
21
+ * - **A chat this process holds is probed in memory.** The Harness is the
22
+ * writer, so its in-memory events answer \"has anything been appended?\" with
23
+ * no stored read at all, and only the appended events are projected. This is
24
+ * the common case: the bridge reads back the chat it just asked in.
25
+ * - **A chat this process does not hold is trusted for a bounded time.** Nobody
26
+ * in this process can append to it, so the window can only be wrong about a
27
+ * write from somewhere else sharing the same storage — which no page read can
28
+ * distinguish either. The bound is deliberately the same budget the review
29
+ * console gives its own transcript cache, so the two answer a reviewer the
30
+ * same way.
31
+ *
32
+ * A window is never *replaced* by an in-memory snapshot, only extended from it:
33
+ * a session that outlived a process restart, or a very long one, may hold only
34
+ * part of its history in memory, and a read that trusted it as the whole log
35
+ * would answer a page from half a conversation.
36
+ *
37
+ * What it costs: at most one window per conversation, and a window is at most
38
+ * one maximal page — so the memory this keeps is bounded by
39
+ * {@link QA_INTEGRATION_TRANSCRIPT_CACHE_CHATS} pages of conversation, and a
40
+ * page a caller would have received anyway. An old chat's *first* read is still
41
+ * a stored read, and its projection walks back only as far as the window it is
42
+ * building: the whole log of a long conversation is never flattened to answer a
43
+ * page of it.
44
+ */
45
+ /** How long a window is trusted for a chat this process does not hold. */
46
+ export declare const QA_INTEGRATION_TRANSCRIPT_TTL_MS = 30000;
47
+ /** How many conversations keep a warm window. */
48
+ export declare const QA_INTEGRATION_TRANSCRIPT_CACHE_CHATS = 128;
49
+ export interface QaIntegrationTranscriptReaderOptions {
50
+ /** The deployment's conversations, as the Host reads them. */
51
+ log: QaSessionLogReader;
52
+ logger?: PluginLogger;
53
+ /** Injectable clock, so the trust window is testable without waiting. */
54
+ now?: () => number;
55
+ ttlMs?: number;
56
+ maxChats?: number;
57
+ /** Ceiling on the messages a window holds; one maximal page by default. */
58
+ maxMessages?: number;
59
+ }
60
+ export declare class QaIntegrationTranscriptReader {
61
+ private readonly windows;
62
+ private readonly log;
63
+ private readonly logger;
64
+ private readonly now;
65
+ private readonly ttlMs;
66
+ private readonly maxChats;
67
+ private readonly maxMessages;
68
+ constructor(options: QaIntegrationTranscriptReaderOptions);
69
+ /**
70
+ * One page of a conversation, from the window when the window can answer it.
71
+ * @param chatId - the conversation; the caller's ownership of it is the
72
+ * service's business, and this reader only reads it.
73
+ * @param query - the caller's exclusive cursor and the page size it wants.
74
+ * @returns the newest page after the cursor, with its new cursor.
75
+ */
76
+ page(chatId: string, query: {
77
+ readonly after: number;
78
+ readonly limit: number;
79
+ }): Promise<QaIntegrationTranscript>;
80
+ /** One page out of a window: exact, whatever the caller's cursor is. */
81
+ private pageOf;
82
+ /**
83
+ * Fold what the Harness is holding into a window, projecting only the events
84
+ * the window has not covered yet.
85
+ * @returns whether the window absorbed the update; false means the delta is
86
+ * larger than a whole window, so the log has to answer instead.
87
+ */
88
+ private merge;
89
+ /**
90
+ * The newest published seq a window has covered, which is also the cursor a
91
+ * probe is asked from: `undefined` while the window holds nothing, where the
92
+ * question is "what has this chat published at all".
93
+ */
94
+ private covered;
95
+ /** Read the log and remember what it says, for the pages that follow. */
96
+ private readLog;
97
+ /** Keep a bounded number of conversations warm, oldest window first. */
98
+ private remember;
99
+ }
100
+ //# sourceMappingURL=transcript-reader.d.ts.map
@@ -0,0 +1,59 @@
1
+ import type { StoredSessionEvent } from "../admin/conversation-log.js";
2
+ import type { QaIntegrationMessage, QaIntegrationTranscript } from "./contract.js";
3
+ /**
4
+ * Reading a conversation back out of its durable log, for the application that
5
+ * started it.
6
+ *
7
+ * This is deliberately not the admin viewer's projection. That one answers a
8
+ * reviewer and carries what a review needs — tool calls, ratings, the redaction
9
+ * policy — none of which an external caller may see or has any use for. What
10
+ * this projector shares with the rest of the API is the flattening rule
11
+ * (`messageContentText`): the text a caller reads here is the same text it
12
+ * would have received as an answer, so the two never disagree about what a
13
+ * message says.
14
+ *
15
+ * Only human prompts and assistant messages are published. Injected context —
16
+ * identity notes, skill payloads, a slash command's expanded body — is model
17
+ * input recorded as a user message, and handing it back would show the caller
18
+ * instructions it never wrote. Tool traffic is model plumbing: the answer is
19
+ * already the result of it.
20
+ */
21
+ /** Newest messages one read returns; more than a page is a different tool. */
22
+ export declare const QA_INTEGRATION_MAX_TRANSCRIPT_MESSAGES = 200;
23
+ /**
24
+ * The newest published messages of a log, and what was left below them.
25
+ *
26
+ * This is the primitive both readers are built on, and it walks the events
27
+ * **newest first**: a page of a hundred-message conversation costs a hundred
28
+ * messages flattened, not the whole log, which is what makes a long chat's
29
+ * page cheap. Only the events below the cursor are considered, and the walk
30
+ * stops as soon as the window is full — the one message after that is enough
31
+ * to know that more exists, and `below` records its seq.
32
+ *
33
+ * @param chatId - the conversation being read; echoed back for correlation.
34
+ * @param events - the log's events, in any order.
35
+ * @param query - the cursor (exclusive) and how many messages to cover; the
36
+ * cursor is `undefined` for "from the end", which a warm window asks for.
37
+ * @returns the window, oldest first, and the newest seq left below it.
38
+ */
39
+ export declare function integrationMessageWindow(chatId: string, events: readonly StoredSessionEvent[], query: {
40
+ readonly after: number | undefined;
41
+ readonly cover: number;
42
+ }): {
43
+ readonly chatId: string;
44
+ readonly messages: readonly QaIntegrationMessage[];
45
+ readonly below: number | undefined;
46
+ };
47
+ /**
48
+ * Project the log into one page of a caller's transcript.
49
+ *
50
+ * @param chatId - the conversation being read; echoed back for correlation.
51
+ * @param events - durable events, in any order.
52
+ * @param query - the caller's cursor and page size; both are already bounded.
53
+ * @returns the window with the caller's new cursor.
54
+ */
55
+ export declare function projectIntegrationTranscript(chatId: string, events: readonly StoredSessionEvent[], query: {
56
+ readonly after: number;
57
+ readonly limit: number;
58
+ }): QaIntegrationTranscript;
59
+ //# sourceMappingURL=transcript.d.ts.map
@@ -2,8 +2,8 @@ export { QaPersonalSkillError, type QaPersonalSkillErrorReason, } from "./errors
2
2
  export { QA_SKILL_TRASH_SUFFIX, resolveSkillRoots, skillDirectory, skillFilePath, type QaSkillRoots, } from "./paths.js";
3
3
  export { normalizeAllowedTools, parseSkillFile, serializeSkillFile, skillFileBody, skillNameProblem, toJsonValue, type QaSkillFileContents, type QaSkillFileDraft, type QaSkillFileParse, } from "./skill-file.js";
4
4
  export { skillFileBytes, skillRelativeRootProblem, skillRelativeRootSegments, validateSkillDraft, QA_SKILL_DEFAULT_RELATIVE_ROOT, QA_SKILL_DESCRIPTION_MAX, QA_SKILL_ENABLED_BY_DEFAULT, QA_SKILL_FILE_MAX_BYTES, QA_SKILL_MAX_BYTES_MAX, QA_SKILL_MAX_BYTES_MIN, QA_SKILL_MAX_TOOLS, QA_SKILL_NAME_MAX, QA_SKILL_NAME_PATTERN, QA_SKILL_WHEN_TO_USE_MAX, type QaSkillValidationInput, } from "./skill-format.js";
5
- export { QA_USER_SKILLS_PROVIDER, QA_USER_SKILLS_RANK, QA_USER_SKILLS_SOURCE, createQaUserSkillProvider, } from "./provider.js";
6
- export { QaPersonalSkills, type QaPersonalSkillContext, type QaPersonalSkillsOptions, type QaStoredSkill, } from "./service.js";
5
+ export { QA_SHARED_SKILLS_RANK, QA_SHARED_SKILLS_SOURCE, QA_USER_SKILLS_PROVIDER, QA_USER_SKILLS_RANK, QA_USER_SKILLS_SOURCE, createQaUserSkillProvider, } from "./provider.js";
6
+ export { QA_SKILL_ADMIN_EDITS_FILE, QaPersonalSkills, type QaPersonalSkillContext, type QaPersonalSkillsOptions, type QaSharedSkillContext, type QaSkillScope, type QaSkillWriteOrigin, type QaStoredSkill, } from "./service.js";
7
7
  export { createQaPersonalSkillRemotes, type QaPersonalSkillRemotes, } from "./remotes.js";
8
8
  export { QaPersonalSkillsHost } from "./host.js";
9
9
  export { QaSkillWatcher } from "./watcher.js";
@@ -20,11 +20,23 @@ export interface QaSkillRoots {
20
20
  readonly skills: string;
21
21
  /** The canonical directory a removed skill directory is moved into. */
22
22
  readonly trash: string;
23
+ /**
24
+ * Whether these roots are the deployment-wide store rather than one
25
+ * account's. The geometry is identical, so the flag travels with the roots
26
+ * instead of being re-derived from them at every use.
27
+ */
28
+ readonly shared: boolean;
23
29
  }
24
30
  /** Derive and verify every directory the feature touches for one account. */
25
- export declare function resolveSkillRoots(personalRoot: string, relativeRoot: string): QaSkillRoots;
26
- /** The directory of one skill, validated against the account's skills root. */
27
- export declare function skillDirectory(roots: QaSkillRoots, name: string): string;
31
+ export declare function resolveSkillRoots(personalRoot: string, relativeRoot: string,
32
+ /**
33
+ * What the boundary refuses to leave, for the message an operator reads.
34
+ * The same resolution serves an account directory and the deployment-wide
35
+ * shared root, which have different owners but identical geometry.
36
+ */
37
+ boundary?: string): QaSkillRoots;
38
+ /** The directory of one skill, validated against the scope's skills root. */
39
+ export declare function skillDirectory(roots: QaSkillRoots, name: string, boundary?: string): string;
28
40
  export declare function skillFilePath(roots: QaSkillRoots, name: string): string;
29
41
  /** Whether the directory exists at all, without following a symlink. */
30
42
  export declare function directoryExists(input: string): boolean;
@@ -7,8 +7,11 @@ import type { QaPersonalSkills } from "./service.js";
7
7
  * through the nearest `.git`, which for a personal QA directory inside a larger
8
8
  * checkout would climb above the account and mix users together. This provider
9
9
  * reads exactly one place — `<cwd>/.dsh/skills` where the cwd is a provisioned
10
- * `.qa-users/<uuid>` directory — and replaces the filesystem layer inside the
11
- * QA scope rather than adding a second one beside it.
10
+ * `.qa-users/<uuid>` directory — and the deployment's shared root beside the
11
+ * registered workspace, replacing the filesystem layer inside the QA scope
12
+ * rather than adding a second one beside it. A shared skill is labelled and
13
+ * ranked distinctly, so a load can tell the deployment's own instructions from
14
+ * the account's.
12
15
  */
13
16
  export declare const QA_USER_SKILLS_PROVIDER = "qa-user-skills";
14
17
  /** Prompt-visible provenance label for a skill stored in an account directory. */
@@ -18,6 +21,15 @@ export declare const QA_USER_SKILLS_SOURCE = "qa-user";
18
21
  * QA scope a personal skill wins a same-named duplicate from any other layer.
19
22
  */
20
23
  export declare const QA_USER_SKILLS_RANK = 50;
24
+ /** Prompt-visible provenance label for a deployment-wide shared skill. */
25
+ export declare const QA_SHARED_SKILLS_SOURCE = "qa-shared";
26
+ /**
27
+ * Rank between the personal layer (50) and the project layer (100). A shared
28
+ * skill is curated for the whole deployment, so it outranks what a checkout
29
+ * happens to carry — but one account's own copy of the same name still wins,
30
+ * because that is the layer a person deliberately edited about themselves.
31
+ */
32
+ export declare const QA_SHARED_SKILLS_RANK = 60;
21
33
  /**
22
34
  * Build the provider for one registration. The factory runs synchronously
23
35
  * during `registerProvider` and all filesystem work stays inside
@@ -2,15 +2,48 @@ import type { Context } from "@deepseek-ai/cordis";
2
2
  import type { PluginLogger } from "@yadsh/dsh-plugin-log";
3
3
  import type { QaSkillDiagnostic, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillSummary, QaSkillToolDescriptor, QaSkillValidation, ResolvedQaSurfaceConfig } from "../types.js";
4
4
  import { type QaSkillFileContents } from "./skill-file.js";
5
+ /**
6
+ * Name of the sidecar that records which skills an administrator wrote. It
7
+ * sits beside the skill directories, and everything that enumerates skills
8
+ * ignores dot entries — so the record never becomes a skill and never reaches
9
+ * the model.
10
+ */
11
+ export declare const QA_SKILL_ADMIN_EDITS_FILE = ".admin-edits.json";
5
12
  /** Per-account identity of the skill storage boundary. */
6
13
  export interface QaPersonalSkillContext {
7
14
  /** The authenticated account id whose personal root holds these skills. */
8
15
  readonly userId: string;
9
16
  }
17
+ /**
18
+ * The deployment-wide store: skills every account may read, which only an
19
+ * administrator writes. It is resolved against the registered workspace (the
20
+ * same relative root, a different owner), so the two scopes have identical
21
+ * geometry and identical path checks.
22
+ */
23
+ export interface QaSharedSkillContext {
24
+ readonly shared: true;
25
+ }
26
+ /** Which store a call reads or writes. */
27
+ export type QaSkillScope = QaPersonalSkillContext | QaSharedSkillContext;
28
+ /**
29
+ * What a save must record about itself beyond the draft.
30
+ *
31
+ * `actorId` is the administrator behind an administrator's write. An owner's
32
+ * own save passes nothing, which clears whatever mark the skill carried: the
33
+ * stored revision is theirs again, and the badge is a statement about the
34
+ * bytes on disk rather than a history nobody can act on. Removal takes no
35
+ * origin because a removed skill has no bytes left to mark — the audit row the
36
+ * console writes is where that actor is named.
37
+ */
38
+ export interface QaSkillWriteOrigin {
39
+ readonly actorId?: string;
40
+ }
10
41
  /** One stored skill: its parsed contents plus the facts a DTO needs. */
11
42
  export interface QaStoredSkill {
12
43
  /** Whether the skill directory itself exists. */
13
44
  readonly exists: boolean;
45
+ /** Whether the skill came from the deployment-wide store. */
46
+ readonly shared: boolean;
14
47
  /** Directory name; the skill's identity for every write. */
15
48
  readonly directoryName: string;
16
49
  readonly directory: string;
@@ -34,9 +67,9 @@ export interface QaPersonalSkillsOptions {
34
67
  readonly onRootDiscovered?: (root: string) => void;
35
68
  }
36
69
  /**
37
- * Personal Skills over one account's own directory.
70
+ * Skills over one directory: an account's own, or the deployment's shared one.
38
71
  *
39
- * The service is the only writer: it derives every path from the account id,
72
+ * The service is the only writer: it derives every path from the scope,
40
73
  * validates a draft before touching the disk, writes `SKILL.md` through a
41
74
  * temporary file and a rename, and keeps a removed skill recoverable in the
42
75
  * trash beside its skills root. Reading is deliberately permissive — a skill
@@ -49,8 +82,19 @@ export declare class QaPersonalSkills {
49
82
  constructor(ctx: Context, options: QaPersonalSkillsOptions);
50
83
  /** Whether the feature is available on this deployment at all. */
51
84
  get enabled(): boolean;
52
- /** Resolve one account's storage roots, or refuse the operation. */
85
+ /** Resolve one scope's storage roots, or refuse the operation. */
53
86
  private rootsFor;
87
+ /** The deployment's own skills root, below the registered workspace. */
88
+ private sharedRoots;
89
+ private requireEnabled;
90
+ /** The registered workspace every scope is resolved against, or a refusal. */
91
+ private requireWorkspacePath;
92
+ /**
93
+ * The absolute directory a scope reads and writes. The console names it so
94
+ * an operator can open the same path in a file manager; nothing in the wire
95
+ * contract can name a path the service did not derive itself.
96
+ */
97
+ rootPath(scope: QaSkillScope): string;
54
98
  /**
55
99
  * Resolve the storage roots a session cwd names. This is the discovery
56
100
  * provider's entry point and takes no account token: the cwd itself is the
@@ -58,10 +102,10 @@ export declare class QaPersonalSkills {
58
102
  * directory, so no arbitrary directory can name another account's storage.
59
103
  */
60
104
  private rootsForCwd;
61
- /** Every skill of one account, sorted by directory name. */
62
- list(context: QaPersonalSkillContext): readonly QaSkillSummary[];
105
+ /** Every skill of one scope, sorted by directory name. */
106
+ list(context: QaSkillScope): readonly QaSkillSummary[];
63
107
  /** One skill with everything the editor needs. */
64
- get(context: QaPersonalSkillContext, name: string): QaSkillDocument;
108
+ get(context: QaSkillScope, name: string): QaSkillDocument;
65
109
  /**
66
110
  * Check one unsaved draft without touching storage: the serializer is the
67
111
  * one a Save uses and the preserved frontmatter is the stored file's, so the
@@ -69,25 +113,25 @@ export declare class QaPersonalSkills {
69
113
  * and the diagnostics carry the operator's own limit (which the browser
70
114
  * cannot know). `name` is the stored skill being edited, or null to create.
71
115
  */
72
- validate(context: QaPersonalSkillContext, name: string | null, input: QaSkillDraftInput): QaSkillValidation;
73
- /** Whether one account stores a skill under this name. */
74
- has(context: QaPersonalSkillContext, name: string): boolean;
116
+ validate(context: QaSkillScope, name: string | null, input: QaSkillDraftInput): QaSkillValidation;
117
+ /** Whether one scope stores a skill under this name. */
118
+ has(context: QaSkillScope, name: string): boolean;
75
119
  /** Create one skill directory and its `SKILL.md`. */
76
- create(context: QaPersonalSkillContext, input: QaSkillDraftInput): QaSkillDocument;
120
+ create(context: QaSkillScope, input: QaSkillDraftInput, origin?: QaSkillWriteOrigin): QaSkillDocument;
77
121
  /**
78
122
  * Replace one skill's file, preserving the frontmatter fields this editor
79
123
  * does not own and moving the whole directory when the name changed.
80
124
  */
81
- update(context: QaPersonalSkillContext, name: string, input: QaSkillDraftInput): QaSkillDocument;
125
+ update(context: QaSkillScope, name: string, input: QaSkillDraftInput, origin?: QaSkillWriteOrigin): QaSkillDocument;
82
126
  /** Move one skill directory into the trash beside its skills root. */
83
- remove(context: QaPersonalSkillContext, name: string, expectedRevision: string | null): QaSkillRemoval;
127
+ remove(context: QaSkillScope, name: string, expectedRevision: string | null): QaSkillRemoval;
84
128
  /**
85
129
  * The tool catalog the picker offers. Availability is the QA session's own
86
130
  * scope — the deployment's reviewed allow-list — never a suggestion of what
87
131
  * a skill may grant: a declared tool the scope excludes stays listed and
88
132
  * stays unavailable.
89
133
  */
90
- tools(context: QaPersonalSkillContext): readonly QaSkillToolDescriptor[];
134
+ tools(context: QaSkillScope): readonly QaSkillToolDescriptor[];
91
135
  /**
92
136
  * The skills one session cwd exposes to DSH. A file the parser rejects, or
93
137
  * one whose name disagrees with its directory, is reported in the editor and
@@ -99,8 +143,18 @@ export declare class QaPersonalSkills {
99
143
  * from every session — not just this account's skills.
100
144
  */
101
145
  discover(cwd: string): readonly QaStoredSkill[];
146
+ /** One roots pair's discoverable skills; never throws. */
147
+ private discoverIn;
148
+ /**
149
+ * The deployment's shared root for one cwd-scoped read, or undefined. It
150
+ * answers undefined when shared skills cannot be resolved at all, and when
151
+ * the shared root is the root the personal read already covered — a session
152
+ * pinned to the workspace root would otherwise list everything twice.
153
+ */
154
+ private sharedRootsForDiscovery;
102
155
  /** Re-read one skill of a session cwd, re-checking the same boundary. */
103
156
  read(cwd: string, directoryName: string): QaStoredSkill | undefined;
157
+ private readFrom;
104
158
  private observe;
105
159
  private isDiscoverable;
106
160
  private availableTools;
@@ -120,7 +174,7 @@ export declare class QaPersonalSkills {
120
174
  /** Move a renamed skill's whole directory, resources included. */
121
175
  private renameSkillDirectory;
122
176
  /** Write one file through a temporary sibling and a rename. */
123
- private writeSkillFile;
177
+ private writeAtomic;
124
178
  private trashTarget;
125
179
  private assertRevision;
126
180
  private summarize;
@@ -132,5 +186,26 @@ export declare class QaPersonalSkills {
132
186
  * is still stored, and the deployment needs to see which half broke.
133
187
  */
134
188
  private refreshCatalog;
189
+ /**
190
+ * The administrator marks stored beside one skills root.
191
+ *
192
+ * The sidecar is advisory: a mark that cannot be read or written never fails
193
+ * a save, because losing a badge is a smaller failure than losing an edit.
194
+ * Each mark names the revision it describes, so a mark whose content has
195
+ * since changed reports as absent rather than as a claim about the present.
196
+ */
197
+ private adminEdits;
198
+ /** The mark to report for one stored skill: absent unless it still matches. */
199
+ private adminEditOf;
200
+ /**
201
+ * Record one administrator write, or clear the mark when the owner wrote.
202
+ * `previousName` is the directory the skill had before the write, so a
203
+ * rename by an administrator moves the mark with the skill instead of
204
+ * leaving one behind for a name nothing answers to any more.
205
+ */
206
+ private rememberWrite;
207
+ /** Drop the mark of a skill that no longer exists. */
208
+ private forgetWrite;
209
+ private writeAdminEdits;
135
210
  }
136
211
  //# sourceMappingURL=service.d.ts.map
@@ -14,6 +14,21 @@ export declare const QA_IDENTITY_NOTE = "dsh-qa-surface:user-identity";
14
14
  export declare const QA_SOURCES_NOTE = "dsh-qa-surface:structured-sources";
15
15
  /** Note name asking the model to name its delegations. */
16
16
  export declare const QA_DELEGATION_NOTE = "dsh-qa-surface:delegation-naming";
17
+ /** Note name routing an attached office document to the document pipeline. */
18
+ export declare const QA_DOCUMENTS_NOTE = "dsh-qa-surface:attached-documents";
19
+ /** Note name carrying the deployment's rule about where an answer comes from. */
20
+ export declare const QA_SOURCE_PRIORITY_NOTE = "dsh-qa-surface:source-priority";
21
+ /**
22
+ * Built-in note texts. Each is the fallback for its settings template
23
+ * (`notes.*.template`); the placeholders mark the parts the injector
24
+ * substitutes. Unknown or empty placeholders render as empty text.
25
+ */
26
+ export declare const QA_IDENTITY_NOTE_TEMPLATE = "{identity}\n\n{instructions}";
27
+ export declare const QA_SOURCES_NOTE_TEMPLATE = "Source provenance is collected automatically from your tool calls; the QA surface lists what it collected beside the answer. Do not append a manual Sources/\u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0438 bibliography of your own.";
28
+ export declare const QA_SOURCES_FALLBACK_TEMPLATE = "A delegated run whose provider cannot expose tool events must call {reportTool} before finishing.";
29
+ export declare const QA_DELEGATION_NOTE_TEMPLATE = "When you start a background subagent, give the delegation a short vivid name in its description field: two or three words in the user's language that say what the run is for (\u00AB\u0421\u0432\u0435\u0440\u043A\u0430 \u043E\u0442\u0447\u0451\u0442\u043E\u0432\u00BB, \"Log triage\"). The QA surface shows that description as the subagent's display name in the operator's panel and completion notices.";
30
+ export declare const QA_DOCUMENTS_NOTE_TEMPLATE = "A document a chat user attached is a path on disk, not text: read it with the deployment's document pipeline \u2014 `document_inspect` says what a DOCX or PDF is, `document_to_markdown` extracts its text, `document_from_url` stores the text of an attachment that only exists behind a URL. The plain file reader refuses those formats as binary, so that refusal is expected and is not a hint to go looking for the file somewhere else. When the pipeline itself refuses a path, report the refusal and the path it named instead of trying another reader.";
31
+ export declare const QA_SOURCE_PRIORITY_NOTE_TEMPLATE = "Answer from the source that owns the question: read what the conversation and its attachments already carry, then search the product documentation and ask the domain expert, and only then look in memory. Recalled memory is background from earlier sessions \u2014 a miss there is not evidence that no source exists, and it is not where a document, a page or a product fact should be looked up.";
17
32
  export interface QaIdentityRenderInput {
18
33
  /** The owner's account address; the one fact accounts always provide. */
19
34
  readonly email: string;
@@ -98,6 +113,28 @@ export declare class QaPromptNotes {
98
113
  * the difference between a panel of hashes and a panel of tasks.
99
114
  */
100
115
  private delegationNotes;
116
+ /**
117
+ * Which reader an attached document belongs to.
118
+ *
119
+ * A chat user's `.docx` or `.pdf` arrives as a path into the attachment
120
+ * store, and the reader the model reaches for by habit answers `binary file`
121
+ * for it. That answer is a fact about the format, not about the file being
122
+ * missing, and a run that reads it as the second one searches the workspace
123
+ * for a document it was already handed. The note names the pipeline and both
124
+ * halves of the trap: the plain reader's refusal is expected, and the
125
+ * pipeline's own refusal is a report to make, not a reason to try a third
126
+ * reader.
127
+ */
128
+ private documentsNotes;
129
+ /**
130
+ * Where an answer comes from, in this deployment's terms.
131
+ *
132
+ * The memory plugin's own skill says the same thing in its own words, but a
133
+ * persona may be the entire system prompt and a skill is read only once the
134
+ * model decides to reach for it: the rule that outlives both is the one
135
+ * delivered as a note, ahead of the first tool call that would break it.
136
+ */
137
+ private sourcePriorityNotes;
101
138
  private carriedKey;
102
139
  /**
103
140
  * The text one note already carries in this agent's conversation. Process
@@ -0,0 +1,28 @@
1
+ import type { DocumentsFace } from "@yadsh/dsh-documents";
2
+ import type { QaDocumentPreview } from "../types.js";
3
+ import { type QaAllowedFileRequest } from "./file-preview.js";
4
+ /** Look up the document subsystem a sibling Host plugin published. */
5
+ export type DocumentsLookup = () => DocumentsFace | undefined;
6
+ export interface QaDocumentPreviewRequest extends QaAllowedFileRequest {
7
+ /** The document pipeline, resolved per call so a late provider still counts. */
8
+ readonly documents: DocumentsLookup;
9
+ readonly sessionId: string;
10
+ /** Bytes of the rendered PDF the panel accepts before it gives up. */
11
+ readonly maxBytes: number;
12
+ }
13
+ /**
14
+ * Render one Word document as a PDF for the files panel.
15
+ *
16
+ * The conversion is the document pipeline's own — the same runtime, providers
17
+ * and limits the `document_*` tools use — reached through the face that plugin
18
+ * publishes for its Host siblings; a deployment without the pipeline installed
19
+ * refuses with `unsupported` rather than growing a second converter here. The
20
+ * input path passes the same containment check the panel's other reads use, so
21
+ * a preview cannot read what a listing could not reach.
22
+ *
23
+ * The resolved root the check opened is handed to the pipeline as well (§26.2):
24
+ * an uploaded Word file lives in the attachment store, and a pipeline that is
25
+ * not told about that root refuses the file this check just accepted.
26
+ */
27
+ export declare function previewConvertibleDocument({ documents, sessionId, maxBytes, ...file }: QaDocumentPreviewRequest): Promise<QaDocumentPreview>;
28
+ //# sourceMappingURL=document-preview.d.ts.map
@@ -1,11 +1,13 @@
1
- import type { QaSourceFilePreview } from "../types.js";
1
+ import type { QaSourceFilePreview, QaWorkspaceFile, QaWorkspaceListing } from "../types.js";
2
2
  /**
3
3
  * Coarse, wire-safe preview refusal codes. They ride the shared
4
4
  * `(reason: <code>)` marker, so the panel can tell "this file is not in a
5
5
  * directory the QA read policy opens" from "this file is gone" instead of
6
6
  * telling every audience that a present file moved away.
7
7
  */
8
- export type QaSourcePreviewReason = "outside-roots" | "not-evidence" | "unavailable";
8
+ export type QaSourcePreviewReason = "outside-roots" | "not-evidence" | "unavailable"
9
+ /** The panel has no way to render this format at all. */
10
+ | "unsupported";
9
11
  /** Host refusal whose wire message carries the coarse reason marker. */
10
12
  export declare class QaSourcePreviewError extends Error {
11
13
  readonly reason: QaSourcePreviewReason;
@@ -39,6 +41,32 @@ export interface QaSourceFilePreviewRequest {
39
41
  readonly maxBytes: number;
40
42
  readonly maxMarkdownRenderBytes: number;
41
43
  }
44
+ /** A file request the panel may make: the chat's own tree or a shared root. */
45
+ export interface QaAllowedFileRequest {
46
+ readonly filePath: string;
47
+ /** The attested chat's cwd: the anchor a relative request resolves against. */
48
+ readonly cwd: string;
49
+ readonly sharedReadOnlyRoots?: readonly string[];
50
+ readonly attachmentRoot?: string;
51
+ }
52
+ /** One resolved file that passed the read policy. */
53
+ interface QaAllowedFile {
54
+ /** The path as the caller spelled it, canonicalized against the chat cwd. */
55
+ readonly canonicalPath: string;
56
+ /** Its real filesystem path, once symlinks are followed. */
57
+ readonly canonical: string;
58
+ readonly size: number;
59
+ }
60
+ /**
61
+ * Resolve one requested file and check it against the readable roots.
62
+ *
63
+ * The roots mirror the per-user execution guard — the chat's own cwd plus the
64
+ * deployment's shared read-only directories and the attachment store — so a
65
+ * file the model was explicitly allowed to read stays readable in the panel,
66
+ * while anything else is refused with the coarse `outside-roots` reason
67
+ * instead of leaking whether it exists.
68
+ */
69
+ export declare function resolveAllowedFile({ filePath, cwd, sharedReadOnlyRoots, attachmentRoot, }: QaAllowedFileRequest): Promise<QaAllowedFile>;
42
70
  /**
43
71
  * Read one evidence file from the roots the QA read policy opens.
44
72
  *
@@ -50,4 +78,43 @@ export interface QaSourceFilePreviewRequest {
50
78
  * refused as foreign evidence.
51
79
  */
52
80
  export declare function readSourceFilePreview({ sourcePath, cwd, isEvidence, sharedReadOnlyRoots, attachmentRoot, maxBytes, maxMarkdownRenderBytes, }: QaSourceFilePreviewRequest): Promise<QaSourceFilePreview>;
81
+ /** One directory request from the files panel. */
82
+ export interface QaWorkspaceListRequest {
83
+ /** The directory to list, absolute or relative to the chat cwd. Empty = root. */
84
+ readonly dirPath: string;
85
+ readonly cwd: string;
86
+ /** Children one listing carries; the rest is reported as truncated. */
87
+ readonly maxEntries: number;
88
+ }
89
+ /** The advertised type of one file: by extension, else opaque bytes. */
90
+ export declare function mediaTypeOf(path: string): string;
91
+ /**
92
+ * List one directory of the chat's own workspace.
93
+ *
94
+ * Browsing is deliberately narrower than previewing: the panel walks the tree
95
+ * the chat works in, never the deployment's shared read-only roots, so the
96
+ * visitor cannot enumerate the deployment from the rail. The request is
97
+ * confined with the same realpath-then-contain check the preview uses, which
98
+ * is what makes `..` and symlinked directories harmless.
99
+ * Symlinked children are omitted rather than followed: a link inside the
100
+ * workspace may point anywhere, and a tree that silently leaves the chat's
101
+ * directory is worse than one that hides a link.
102
+ */
103
+ export declare function listWorkspaceDirectory({ dirPath, cwd, maxEntries, }: QaWorkspaceListRequest): Promise<QaWorkspaceListing>;
104
+ /** One file request from the files panel. */
105
+ export interface QaWorkspaceFileRequest extends QaAllowedFileRequest {
106
+ readonly maxBytes: number;
107
+ readonly maxMarkdownRenderBytes: number;
108
+ }
109
+ /**
110
+ * Read one file of the chat's workspace for the panel: text when it decodes as
111
+ * UTF-8, base64 bytes when it does not, so a document the agent produced can be
112
+ * offered for download while a note previews inline.
113
+ *
114
+ * Unlike the source preview this is not bound to recorded evidence — the panel
115
+ * browses what the chat's workspace holds — but it is bound to the same roots
116
+ * the model itself may read.
117
+ */
118
+ export declare function readWorkspaceFile({ filePath, cwd, sharedReadOnlyRoots, attachmentRoot, maxBytes, maxMarkdownRenderBytes, }: QaWorkspaceFileRequest): Promise<QaWorkspaceFile>;
119
+ export {};
53
120
  //# sourceMappingURL=file-preview.d.ts.map
@@ -8,7 +8,7 @@ import type { QaToolDescriptor } from "./types.js";
8
8
  *
9
9
  * Bump it whenever the catalog's tool set or their schemas change.
10
10
  */
11
- export declare const QA_TOOL_CATALOG_VERSION = "1";
11
+ export declare const QA_TOOL_CATALOG_VERSION = "3";
12
12
  /** One QA tool reports the activation state of whichever agent calls it. */
13
13
  export interface QaToolsSelfcheckDeps {
14
14
  readonly catalogVersion: string;