@yadsh/dsh-qa-surface 0.15.7 → 0.17.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 (348) hide show
  1. package/README.md +27 -2
  2. package/lib/access/capability-catalog.js +11 -4
  3. package/lib/access/capability-catalog.js.map +1 -1
  4. package/lib/access/service.js +46 -5
  5. package/lib/access/service.js.map +1 -1
  6. package/lib/account-remotes.js +9 -0
  7. package/lib/account-remotes.js.map +1 -1
  8. package/lib/accounts/database.js +34 -3
  9. package/lib/accounts/database.js.map +1 -1
  10. package/lib/accounts/store.js +22 -2
  11. package/lib/accounts/store.js.map +1 -1
  12. package/lib/admin/service.js +4 -2
  13. package/lib/admin/service.js.map +1 -1
  14. package/lib/admin/session-log.js +3 -0
  15. package/lib/admin/session-log.js.map +1 -1
  16. package/lib/agent-scope.js +9 -0
  17. package/lib/agent-scope.js.map +1 -0
  18. package/lib/chat-sharing/native-fence.js +120 -0
  19. package/lib/chat-sharing/native-fence.js.map +1 -0
  20. package/lib/chat-sharing/resources.js +57 -0
  21. package/lib/chat-sharing/resources.js.map +1 -0
  22. package/lib/chat-sharing/store.js +199 -0
  23. package/lib/chat-sharing/store.js.map +1 -0
  24. package/lib/chat-sharing/transport.js +329 -0
  25. package/lib/chat-sharing/transport.js.map +1 -0
  26. package/lib/chat-sharing/turn-guard.js +154 -0
  27. package/lib/chat-sharing/turn-guard.js.map +1 -0
  28. package/lib/chat-sharing/types.js +2 -0
  29. package/lib/chat-sharing/types.js.map +1 -0
  30. package/lib/chat-sharing/uploads.js +55 -0
  31. package/lib/chat-sharing/uploads.js.map +1 -0
  32. package/lib/client/QaAccountsController.js +47 -2
  33. package/lib/client/QaAccountsController.js.map +1 -1
  34. package/lib/client/QaConfigController.js +7 -12
  35. package/lib/client/QaConfigController.js.map +1 -1
  36. package/lib/client/QaSessionController.js +195 -19
  37. package/lib/client/QaSessionController.js.map +1 -1
  38. package/lib/client/QaSurface.js +156 -14
  39. package/lib/client/QaSurface.js.map +1 -1
  40. package/lib/client/QaSurfaceGuard.js +6 -1
  41. package/lib/client/QaSurfaceGuard.js.map +1 -1
  42. package/lib/client/QaTranscriptAdapter.js +28 -5
  43. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  44. package/lib/client/account-conversation.js +33 -0
  45. package/lib/client/account-conversation.js.map +1 -0
  46. package/lib/client/account-sessions.js +106 -0
  47. package/lib/client/account-sessions.js.map +1 -0
  48. package/lib/client/account-upload.js +30 -0
  49. package/lib/client/account-upload.js.map +1 -0
  50. package/lib/client/answer-review.js +89 -0
  51. package/lib/client/answer-review.js.map +1 -0
  52. package/lib/client/chat-artifacts.js +18 -3
  53. package/lib/client/chat-artifacts.js.map +1 -1
  54. package/lib/client/chat-sharing-api.js +2 -0
  55. package/lib/client/chat-sharing-api.js.map +1 -0
  56. package/lib/client/components/QaAnswerReviewStatus.js +45 -0
  57. package/lib/client/components/QaAnswerReviewStatus.js.map +1 -0
  58. package/lib/client/components/QaChangelog.js +92 -3
  59. package/lib/client/components/QaChangelog.js.map +1 -1
  60. package/lib/client/components/QaChatActions.js +52 -0
  61. package/lib/client/components/QaChatActions.js.map +1 -0
  62. package/lib/client/components/QaChatSharing.js +95 -0
  63. package/lib/client/components/QaChatSharing.js.map +1 -0
  64. package/lib/client/components/QaComposer.js +8 -4
  65. package/lib/client/components/QaComposer.js.map +1 -1
  66. package/lib/client/components/QaFileAttachment.js +12 -3
  67. package/lib/client/components/QaFileAttachment.js.map +1 -1
  68. package/lib/client/components/QaFilesPanel.js +11 -4
  69. package/lib/client/components/QaFilesPanel.js.map +1 -1
  70. package/lib/client/components/QaHeader.js +3 -3
  71. package/lib/client/components/QaHeader.js.map +1 -1
  72. package/lib/client/components/QaMarkdownPreview.js +86 -0
  73. package/lib/client/components/QaMarkdownPreview.js.map +1 -0
  74. package/lib/client/components/QaMessage.js +41 -10
  75. package/lib/client/components/QaMessage.js.map +1 -1
  76. package/lib/client/components/QaModal.js +17 -0
  77. package/lib/client/components/QaModal.js.map +1 -1
  78. package/lib/client/components/QaRightRail.js +6 -4
  79. package/lib/client/components/QaRightRail.js.map +1 -1
  80. package/lib/client/components/QaRightRailResize.js +129 -0
  81. package/lib/client/components/QaRightRailResize.js.map +1 -0
  82. package/lib/client/components/QaSidebar.js +11 -2
  83. package/lib/client/components/QaSidebar.js.map +1 -1
  84. package/lib/client/components/QaSourceFilters.js +22 -0
  85. package/lib/client/components/QaSourceFilters.js.map +1 -0
  86. package/lib/client/components/QaSourcesPanel.js +66 -14
  87. package/lib/client/components/QaSourcesPanel.js.map +1 -1
  88. package/lib/client/components/QaSpreadsheetPreview.js +56 -0
  89. package/lib/client/components/QaSpreadsheetPreview.js.map +1 -0
  90. package/lib/client/components/QaWelcomeNotice.js +2 -2
  91. package/lib/client/components/QaWelcomeNotice.js.map +1 -1
  92. package/lib/client/components/QaWorkspaceBrowser.js +46 -12
  93. package/lib/client/components/QaWorkspaceBrowser.js.map +1 -1
  94. package/lib/client/components/QaZoomImage.js +56 -0
  95. package/lib/client/components/QaZoomImage.js.map +1 -0
  96. package/lib/client/components/source-refs.js +57 -24
  97. package/lib/client/components/source-refs.js.map +1 -1
  98. package/lib/client/index.js +76 -8
  99. package/lib/client/index.js.map +1 -1
  100. package/lib/client/lineage.js +4 -3
  101. package/lib/client/lineage.js.map +1 -1
  102. package/lib/client/markdown/SourceImage.js +52 -0
  103. package/lib/client/markdown/SourceImage.js.map +1 -0
  104. package/lib/client/markdown/blocks.js +150 -115
  105. package/lib/client/markdown/blocks.js.map +1 -1
  106. package/lib/client/markdown/katex-css.js +1 -1
  107. package/lib/client/markdown/render.js +26 -9
  108. package/lib/client/markdown/render.js.map +1 -1
  109. package/lib/client/markdown/source-chip.js +6 -2
  110. package/lib/client/markdown/source-chip.js.map +1 -1
  111. package/lib/client/notifications/notification-dispatcher.js +50 -11
  112. package/lib/client/notifications/notification-dispatcher.js.map +1 -1
  113. package/lib/client/notifications/use-turn-notifications.js +107 -18
  114. package/lib/client/notifications/use-turn-notifications.js.map +1 -1
  115. package/lib/client/project-session-state.js +28 -13
  116. package/lib/client/project-session-state.js.map +1 -1
  117. package/lib/client/session-admission.js +9 -1
  118. package/lib/client/session-admission.js.map +1 -1
  119. package/lib/client/session-sources.js +44 -3
  120. package/lib/client/session-sources.js.map +1 -1
  121. package/lib/client/settings/sections/sources.js +1 -1
  122. package/lib/client/settings/sections/sources.js.map +1 -1
  123. package/lib/client/settings/styles.js +18 -28
  124. package/lib/client/settings/styles.js.map +1 -1
  125. package/lib/client/slash/useSlashNavigation.js +8 -5
  126. package/lib/client/slash/useSlashNavigation.js.map +1 -1
  127. package/lib/client/source-filters-styles.js +11 -0
  128. package/lib/client/source-filters-styles.js.map +1 -0
  129. package/lib/client/source-preview.js +1 -1
  130. package/lib/client/source-preview.js.map +1 -1
  131. package/lib/client/styles.js +62 -4
  132. package/lib/client/styles.js.map +1 -1
  133. package/lib/client/turn-sources.js +2 -1
  134. package/lib/client/turn-sources.js.map +1 -1
  135. package/lib/client/types.js.map +1 -1
  136. package/lib/client/use-right-rail.js +1 -1
  137. package/lib/client/use-right-rail.js.map +1 -1
  138. package/lib/client/user-settings/IntegrationTokensPage.js +30 -6
  139. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -1
  140. package/lib/client/workspace-download.js +42 -2
  141. package/lib/client/workspace-download.js.map +1 -1
  142. package/lib/client.js +6481 -2234
  143. package/lib/client.js.map +1 -1
  144. package/lib/config-resolvers/defaults.js +4 -3
  145. package/lib/config-resolvers/defaults.js.map +1 -1
  146. package/lib/config-resolvers/sources.js +2 -0
  147. package/lib/config-resolvers/sources.js.map +1 -1
  148. package/lib/config.js +3 -0
  149. package/lib/config.js.map +1 -1
  150. package/lib/enforcement/skill-policy.js +29 -4
  151. package/lib/enforcement/skill-policy.js.map +1 -1
  152. package/lib/enforcement/skill-resource-paths.js +32 -0
  153. package/lib/enforcement/skill-resource-paths.js.map +1 -0
  154. package/lib/enforcement/structured-result.js +38 -0
  155. package/lib/enforcement/structured-result.js.map +1 -0
  156. package/lib/enforcement/tool-grants.js +3 -1
  157. package/lib/enforcement/tool-grants.js.map +1 -1
  158. package/lib/index.js +447 -17
  159. package/lib/index.js.map +1 -1
  160. package/lib/integration/host-runner.js +1 -0
  161. package/lib/integration/host-runner.js.map +1 -1
  162. package/lib/integration/question-policy.js +126 -0
  163. package/lib/integration/question-policy.js.map +1 -0
  164. package/lib/integration/service.js +2 -0
  165. package/lib/integration/service.js.map +1 -1
  166. package/lib/llm-identity.js +27 -0
  167. package/lib/llm-identity.js.map +1 -0
  168. package/lib/markdown-frontmatter.js +47 -0
  169. package/lib/markdown-frontmatter.js.map +1 -0
  170. package/lib/markdown-images.js +33 -0
  171. package/lib/markdown-images.js.map +1 -0
  172. package/lib/prompt-notes.js +19 -2
  173. package/lib/prompt-notes.js.map +1 -1
  174. package/lib/provenance/collector.js +15 -1
  175. package/lib/provenance/collector.js.map +1 -1
  176. package/lib/provenance/connectors.js +57 -27
  177. package/lib/provenance/connectors.js.map +1 -1
  178. package/lib/provenance/dedupe.js +25 -1
  179. package/lib/provenance/dedupe.js.map +1 -1
  180. package/lib/provenance/document-preview.js +31 -3
  181. package/lib/provenance/document-preview.js.map +1 -1
  182. package/lib/provenance/extractors.js +88 -4
  183. package/lib/provenance/extractors.js.map +1 -1
  184. package/lib/provenance/file-download.js +56 -0
  185. package/lib/provenance/file-download.js.map +1 -0
  186. package/lib/provenance/file-preview.js +20 -2
  187. package/lib/provenance/file-preview.js.map +1 -1
  188. package/lib/provenance/host-store.js +59 -10
  189. package/lib/provenance/host-store.js.map +1 -1
  190. package/lib/provenance/integration-extractor.js +62 -0
  191. package/lib/provenance/integration-extractor.js.map +1 -0
  192. package/lib/provenance/snapshot-store.js +2 -0
  193. package/lib/provenance/snapshot-store.js.map +1 -1
  194. package/lib/provenance/source-content.js +103 -0
  195. package/lib/provenance/source-content.js.map +1 -0
  196. package/lib/provenance/source-image.js +57 -0
  197. package/lib/provenance/source-image.js.map +1 -0
  198. package/lib/provenance/spreadsheet-preview.js +52 -0
  199. package/lib/provenance/spreadsheet-preview.js.map +1 -0
  200. package/lib/qa-tools/docs-tools.js +14 -0
  201. package/lib/qa-tools/docs-tools.js.map +1 -1
  202. package/lib/questions.js +6 -1
  203. package/lib/questions.js.map +1 -1
  204. package/lib/secure-session.js +9 -3
  205. package/lib/secure-session.js.map +1 -1
  206. package/lib/session-fork-host.js +157 -0
  207. package/lib/session-fork-host.js.map +1 -0
  208. package/lib/session-fork.js +34 -0
  209. package/lib/session-fork.js.map +1 -0
  210. package/lib/session-ownership.js +5 -1
  211. package/lib/session-ownership.js.map +1 -1
  212. package/lib/shared/documents.js +12 -0
  213. package/lib/shared/documents.js.map +1 -1
  214. package/lib/shared/session-lineage.js +6 -0
  215. package/lib/shared/session-lineage.js.map +1 -0
  216. package/lib/slash/draft-target.js +21 -0
  217. package/lib/slash/draft-target.js.map +1 -0
  218. package/lib/slash/remotes.js +39 -4
  219. package/lib/slash/remotes.js.map +1 -1
  220. package/lib/typert.host.js +1869 -163
  221. package/lib/typert.remote-client.d.ts +56 -5
  222. package/lib/typert.remote-client.js +1678 -154
  223. package/lib/types/access/capability-catalog.d.ts +1 -1
  224. package/lib/types/access/service.d.ts +7 -0
  225. package/lib/types/access.js +2 -0
  226. package/lib/types/access.js.map +1 -0
  227. package/lib/types/account-remotes.d.ts +3 -0
  228. package/lib/types/accounts/database.d.ts +5 -0
  229. package/lib/types/accounts/store.d.ts +7 -2
  230. package/lib/types/accounts.js +2 -0
  231. package/lib/types/accounts.js.map +1 -0
  232. package/lib/types/admin/session-log.d.ts +2 -0
  233. package/lib/types/admin.js +2 -0
  234. package/lib/types/admin.js.map +1 -0
  235. package/lib/types/agent-scope.d.ts +4 -0
  236. package/lib/types/answer-review.js +2 -0
  237. package/lib/types/answer-review.js.map +1 -0
  238. package/lib/types/capability.js +2 -0
  239. package/lib/types/capability.js.map +1 -0
  240. package/lib/types/chat-sharing/native-fence.d.ts +11 -0
  241. package/lib/types/chat-sharing/resources.d.ts +5 -0
  242. package/lib/types/chat-sharing/store.d.ts +29 -0
  243. package/lib/types/chat-sharing/transport.d.ts +32 -0
  244. package/lib/types/chat-sharing/turn-guard.d.ts +21 -0
  245. package/lib/types/chat-sharing/types.d.ts +25 -0
  246. package/lib/types/chat-sharing/uploads.d.ts +5 -0
  247. package/lib/types/client/QaAccountsController.d.ts +4 -0
  248. package/lib/types/client/QaConfigController.d.ts +3 -2
  249. package/lib/types/client/QaSessionController.d.ts +17 -3
  250. package/lib/types/client/QaSurface.d.ts +6 -1
  251. package/lib/types/client/account-conversation.d.ts +6 -0
  252. package/lib/types/client/account-sessions.d.ts +7 -0
  253. package/lib/types/client/account-upload.d.ts +15 -0
  254. package/lib/types/client/answer-review.d.ts +16 -0
  255. package/lib/types/client/chat-sharing-api.d.ts +35 -0
  256. package/lib/types/client/components/QaAnswerReviewStatus.d.ts +8 -0
  257. package/lib/types/client/components/QaChatActions.d.ts +7 -0
  258. package/lib/types/client/components/QaChatSharing.d.ts +12 -0
  259. package/lib/types/client/components/QaComposer.d.ts +2 -0
  260. package/lib/types/client/components/QaFileAttachment.d.ts +3 -2
  261. package/lib/types/client/components/QaFilesPanel.d.ts +4 -1
  262. package/lib/types/client/components/QaMarkdownPreview.d.ts +13 -0
  263. package/lib/types/client/components/QaMessage.d.ts +5 -2
  264. package/lib/types/client/components/QaModal.d.ts +9 -0
  265. package/lib/types/client/components/QaRightRail.d.ts +3 -2
  266. package/lib/types/client/components/QaRightRailResize.d.ts +9 -0
  267. package/lib/types/client/components/QaSidebar.d.ts +2 -0
  268. package/lib/types/client/components/QaSourceFilters.d.ts +12 -0
  269. package/lib/types/client/components/QaSpreadsheetPreview.d.ts +8 -0
  270. package/lib/types/client/components/QaWelcomeNotice.d.ts +1 -1
  271. package/lib/types/client/components/QaWorkspaceBrowser.d.ts +8 -1
  272. package/lib/types/client/components/QaZoomImage.d.ts +38 -0
  273. package/lib/types/client/components/source-refs.d.ts +1 -0
  274. package/lib/types/client/lineage.d.ts +2 -2
  275. package/lib/types/client/markdown/SourceImage.d.ts +7 -0
  276. package/lib/types/client/markdown/blocks.d.ts +8 -1
  277. package/lib/types/client/markdown/render.d.ts +5 -1
  278. package/lib/types/client/markdown/source-chip.d.ts +1 -0
  279. package/lib/types/client/notifications/notification-dispatcher.d.ts +10 -6
  280. package/lib/types/client/notifications/use-turn-notifications.d.ts +4 -0
  281. package/lib/types/client/project-session-state.d.ts +1 -0
  282. package/lib/types/client/session-admission.d.ts +2 -0
  283. package/lib/types/client/session-sources.d.ts +1 -0
  284. package/lib/types/client/settings/styles.d.ts +2 -5
  285. package/lib/types/client/source-filters-styles.d.ts +2 -0
  286. package/lib/types/client/turn-sources.d.ts +1 -1
  287. package/lib/types/client/types.d.ts +18 -5
  288. package/lib/types/client/workspace-download.d.ts +9 -1
  289. package/lib/types/config.js +2 -0
  290. package/lib/types/config.js.map +1 -0
  291. package/lib/types/documents.js +2 -0
  292. package/lib/types/documents.js.map +1 -0
  293. package/lib/types/enforcement/skill-resource-paths.d.ts +5 -0
  294. package/lib/types/enforcement/structured-result.d.ts +11 -0
  295. package/lib/types/expert-memory.js +2 -0
  296. package/lib/types/expert-memory.js.map +1 -0
  297. package/lib/types/index.d.ts +42 -3
  298. package/lib/types/integration/contract.d.ts +1 -0
  299. package/lib/types/integration/question-policy.d.ts +20 -0
  300. package/lib/types/integration/service.d.ts +1 -0
  301. package/lib/types/llm-identity.d.ts +20 -0
  302. package/lib/types/markdown-frontmatter.d.ts +10 -0
  303. package/lib/types/markdown-images.d.ts +3 -0
  304. package/lib/types/policy.js +2 -0
  305. package/lib/types/policy.js.map +1 -0
  306. package/lib/types/prompt-notes.d.ts +3 -1
  307. package/lib/types/provenance/connectors.d.ts +4 -1
  308. package/lib/types/provenance/document-preview.d.ts +5 -2
  309. package/lib/types/provenance/extractors.d.ts +2 -0
  310. package/lib/types/provenance/file-download.d.ts +10 -0
  311. package/lib/types/provenance/file-preview.d.ts +2 -1
  312. package/lib/types/provenance/host-store.d.ts +2 -0
  313. package/lib/types/provenance/integration-extractor.d.ts +5 -0
  314. package/lib/types/provenance/source-content.d.ts +19 -0
  315. package/lib/types/provenance/source-image.d.ts +4 -0
  316. package/lib/types/provenance/spreadsheet-preview.d.ts +5 -0
  317. package/lib/types/provenance/types.d.ts +5 -0
  318. package/lib/types/qa-tools/docs-tools.d.ts +2 -0
  319. package/lib/types/quality.js +7 -0
  320. package/lib/types/quality.js.map +1 -0
  321. package/lib/types/questions.d.ts +2 -1
  322. package/lib/types/session-fork-host.d.ts +27 -0
  323. package/lib/types/session-fork.d.ts +7 -0
  324. package/lib/types/session.js +2 -0
  325. package/lib/types/session.js.map +1 -0
  326. package/lib/types/shared/documents.d.ts +2 -0
  327. package/lib/types/shared/session-lineage.d.ts +7 -0
  328. package/lib/types/skills.js +2 -0
  329. package/lib/types/skills.js.map +1 -0
  330. package/lib/types/slash/draft-target.d.ts +7 -0
  331. package/lib/types/slash/remotes.d.ts +7 -0
  332. package/lib/types/slash.js +2 -0
  333. package/lib/types/slash.js.map +1 -0
  334. package/lib/types/types/access.d.ts +43 -0
  335. package/lib/types/types/accounts.d.ts +177 -0
  336. package/lib/types/types/admin.d.ts +286 -0
  337. package/lib/types/types/answer-review.d.ts +11 -0
  338. package/lib/types/types/capability.d.ts +230 -0
  339. package/lib/types/types/config.d.ts +597 -0
  340. package/lib/types/types/documents.d.ts +34 -0
  341. package/lib/types/types/expert-memory.d.ts +35 -0
  342. package/lib/types/types/policy.d.ts +54 -0
  343. package/lib/types/types/quality.d.ts +100 -0
  344. package/lib/types/types/session.d.ts +397 -0
  345. package/lib/types/types/skills.d.ts +143 -0
  346. package/lib/types/types/slash.d.ts +159 -0
  347. package/lib/types/types.d.ts +14 -2180
  348. package/package.json +8 -7
@@ -1,5 +1,6 @@
1
1
  import { bytesToBase64 } from "./base64.js";
2
2
  import { qaStorageNamespace } from "../shared/session-key.js";
3
+ import { slashDraftTarget } from "../slash/draft-target.js";
3
4
  import { resolveSlashRoute } from "./slash/slash-router.js";
4
5
  import { SLASH_DISABLED_COPY, slashAttachmentsCopy, slashExecuteFailureCopy, slashHostRefusalCopy, slashRefusalCopy, slashWithheldCopy, } from "./slash/copy.js";
5
6
  import { canOpenAsCompatibilityReadOnly, QaPolicyAttestationError, } from "./attestation.js";
@@ -98,6 +99,9 @@ export class QaSessionController {
98
99
  listeners = new Set();
99
100
  sessions;
100
101
  createSessionRemote;
102
+ forkSessionRemote;
103
+ forking = false;
104
+ inheritedThroughSeq;
101
105
  conversation;
102
106
  connection;
103
107
  config;
@@ -150,6 +154,9 @@ export class QaSessionController {
150
154
  viewingSubagent = null;
151
155
  connectedOnce;
152
156
  disposed = false;
157
+ chatAccess;
158
+ sharingApi;
159
+ accessPoll;
153
160
  generation = 0;
154
161
  /**
155
162
  * The session the current chat identity names, or null while the chat it
@@ -203,6 +210,7 @@ export class QaSessionController {
203
210
  slashError = null;
204
211
  slashLoadedAt = 0;
205
212
  slashLoading;
213
+ slashReadRevision = 0;
206
214
  /** Last published view; its identity is the render signal. */
207
215
  slashViewCache;
208
216
  /** Bumped to re-open a palette the user dismissed; see `QaSlashView`. */
@@ -212,6 +220,7 @@ export class QaSessionController {
212
220
  constructor(options) {
213
221
  this.sessions = options.sessions;
214
222
  this.createSessionRemote = options.createSession;
223
+ this.forkSessionRemote = options.forkSession;
215
224
  this.conversation = options.conversation;
216
225
  this.connection = options.connection;
217
226
  this.config = options.config;
@@ -243,11 +252,18 @@ export class QaSessionController {
243
252
  this.selectedSubrole = options.initialSubrole ?? null;
244
253
  this.adminPreview = options.adminPreview === true;
245
254
  this.fileUpload = options.fileUpload ?? (() => undefined);
255
+ this.sharingApi = options.sharingApi;
256
+ if (this.sharingApi !== undefined && options.accounts !== undefined)
257
+ this.accessPoll = setInterval(() => {
258
+ void this.refreshChatAccess();
259
+ }, 1000);
246
260
  this.hostSources = new QaHostSourceBridge(this.sourceApi, () => this.accounts?.token() ?? "");
247
261
  this.hostApprovals = new QaHostApprovalBridge(options.approvalApi);
248
262
  this.hostQuestions = new QaHostQuestionBridge(options.questionApi);
249
263
  this.queueRemote = options.queueStatus;
250
- this.timeoutMs = options.timeoutMs ?? 15_000;
264
+ // Cold account catalogs can outlive one short network round trip.
265
+ // Keep bootstrap bounded without rejecting a valid slow restoration.
266
+ this.timeoutMs = options.timeoutMs ?? 60_000;
251
267
  this.streamPublisher = new StreamPublisher(options.streamIntervalMs ?? 66);
252
268
  this.connectedOnce = this.connection.getSnapshot() !== undefined;
253
269
  this.unsubscribeConnection = this.connection.subscribe(() => {
@@ -339,7 +355,7 @@ export class QaSessionController {
339
355
  * call this surface makes — while a human command goes to the Host's command
340
356
  * runtime and never becomes a model message.
341
357
  */
342
- async send(text, attachments = [], pick = null) {
358
+ async send(text, attachments = [], pick = null, fastReview = false) {
343
359
  const prompt = text.trim();
344
360
  // Images ride the prompt inline; files must be staged first, because the
345
361
  // wire carries a receipt rather than bytes.
@@ -405,6 +421,23 @@ export class QaSessionController {
405
421
  return false;
406
422
  }
407
423
  }
424
+ if (fastReview) {
425
+ this.requestQueueNotice = null;
426
+ const target = await this.liveTarget();
427
+ if (target === undefined)
428
+ return false;
429
+ await this.refreshSlashCatalog(true);
430
+ if (this.disposed || this.session !== target)
431
+ return false;
432
+ const command = this.slashEntries.find((entry) => entry.kind === "command" && entry.name === "no-review");
433
+ if (command === undefined) {
434
+ this.operationError =
435
+ "Fast недоступен в этом чате. Выключите Fast, чтобы отправить запрос с автоматическим ревью.";
436
+ this.publish();
437
+ return false;
438
+ }
439
+ return this.sendCommand(`/no-review${prompt === "" ? "" : ` ${prompt}`}`, attachments, files, command, true);
440
+ }
408
441
  this.operationError = null;
409
442
  this.requestQueueNotice = null;
410
443
  // A running turn has nowhere for a new question to land but its queue, and
@@ -542,7 +575,7 @@ export class QaSessionController {
542
575
  * something the deployment does not allow, and retyping the line is a worse
543
576
  * answer than leaving it in place to be edited.
544
577
  */
545
- async sendCommand(line, attachments, files, entry) {
578
+ async sendCommand(line, attachments, files, entry, requireSuccess = false) {
546
579
  if (this.slashApi === undefined) {
547
580
  this.operationError = SLASH_DISABLED_COPY;
548
581
  this.publish();
@@ -594,6 +627,11 @@ export class QaSessionController {
594
627
  this.publish();
595
628
  return false;
596
629
  }
630
+ if (requireSuccess && result.value.outcome.kind === "error") {
631
+ this.operationError = result.value.outcome.text;
632
+ this.publish();
633
+ return false;
634
+ }
597
635
  this.operationError = null;
598
636
  this.publish();
599
637
  return true;
@@ -613,31 +651,40 @@ export class QaSessionController {
613
651
  * skill registry has no browser-facing change event, so a skill installed
614
652
  * while the page is open is only discovered by asking again.
615
653
  */
654
+ slashReadTarget() {
655
+ if (this.session !== undefined)
656
+ return String(this.session.sessionId);
657
+ return this.drafting
658
+ ? slashDraftTarget(this.selectedSubrole, this.adminPreview)
659
+ : undefined;
660
+ }
616
661
  async refreshSlashCatalog(force = false) {
617
- const session = this.session;
618
- if (session === undefined || this.disposed)
662
+ const sessionId = this.slashReadTarget();
663
+ if (sessionId === undefined || this.disposed)
619
664
  return;
620
665
  if (!this.config.slashCommands.enabled || this.compatibilityReadOnly) {
621
- this.applySlashCatalog(String(session.sessionId), null);
666
+ this.applySlashCatalog(sessionId, null);
622
667
  return;
623
668
  }
624
669
  if (this.slashApi === undefined) {
625
- this.applySlashCatalog(String(session.sessionId), undefined);
670
+ this.applySlashCatalog(sessionId, undefined);
626
671
  return;
627
672
  }
628
673
  if (this.slashLoading !== undefined)
629
674
  return this.slashLoading;
630
- if (!force && this.slashSessionId === String(session.sessionId)) {
675
+ if (!force && this.slashSessionId === sessionId) {
631
676
  if (Date.now() - this.slashLoadedAt < SLASH_CATALOG_STALE_MS)
632
677
  return;
633
678
  }
634
- const sessionId = String(session.sessionId);
679
+ const revision = this.slashReadRevision;
635
680
  this.slashState = "loading";
636
681
  this.publish();
637
682
  const operation = (async () => {
638
683
  try {
639
684
  const result = await this.slashApi?.catalog(this.accounts?.token() ?? "", sessionId);
640
- if (result === undefined)
685
+ if (result === undefined ||
686
+ revision !== this.slashReadRevision ||
687
+ this.disposed)
641
688
  return;
642
689
  if (!result.ok) {
643
690
  this.applySlashCatalog(sessionId, undefined);
@@ -647,6 +694,8 @@ export class QaSessionController {
647
694
  }
648
695
  catch (error) {
649
696
  console.error("dsh-qa-surface: slash catalog failed", error);
697
+ if (revision !== this.slashReadRevision || this.disposed)
698
+ return;
650
699
  this.applySlashCatalog(sessionId, undefined);
651
700
  }
652
701
  })().finally(() => {
@@ -663,10 +712,8 @@ export class QaSessionController {
663
712
  * into refusals.
664
713
  */
665
714
  applySlashCatalog(sessionId, catalog) {
666
- if (this.session === undefined ||
667
- String(this.session.sessionId) !== sessionId) {
715
+ if (this.disposed || this.slashReadTarget() !== sessionId)
668
716
  return;
669
- }
670
717
  this.slashSessionId = sessionId;
671
718
  this.slashLoadedAt = Date.now();
672
719
  if (catalog === null) {
@@ -696,13 +743,14 @@ export class QaSessionController {
696
743
  }
697
744
  /** Whether the catalog of the bound chat was read successfully. */
698
745
  slashCatalogReady() {
699
- return this.slashState === "ready" || this.slashState === "loading";
746
+ return ((this.slashState === "ready" || this.slashState === "loading") &&
747
+ this.slashSessionId === this.slashReadTarget());
700
748
  }
701
749
  /** The slash view of the current state; assembled where the state is. */
702
750
  slashView() {
703
751
  const enabled = this.config.slashCommands.enabled &&
704
752
  !this.compatibilityReadOnly &&
705
- this.slashSessionId !== null &&
753
+ this.slashReadTarget() !== undefined &&
706
754
  this.slashApi !== undefined;
707
755
  // Identity is the render signal: this runs on every publish, including
708
756
  // every stream frame of a running turn, and the composer is memoized on
@@ -786,8 +834,8 @@ export class QaSessionController {
786
834
  * hides the marker message, and the consecutive turns read as variants of
787
835
  * the original question (switched in the surface).
788
836
  */
789
- async regenerate() {
790
- return this.send(QA_REGENERATE_MARKER);
837
+ async regenerate(fastReview = false) {
838
+ return this.send(QA_REGENERATE_MARKER, [], null, fastReview);
791
839
  }
792
840
  async stop() {
793
841
  const target = this.session;
@@ -915,12 +963,14 @@ export class QaSessionController {
915
963
  this.unbind();
916
964
  this.operationError = null;
917
965
  this.policyReady = false;
966
+ this.inheritedThroughSeq = undefined;
918
967
  this.state = {
919
968
  ...QA_SESSION_IDLE_STATE,
920
969
  chatKey: this.chatKey,
921
970
  chatsRevision: this.chatsRevision,
922
971
  };
923
972
  this.publish();
973
+ void this.refreshSlashCatalog(true);
924
974
  }
925
975
  /**
926
976
  * Turn the current draft into a real attested session. Runs inside the
@@ -975,6 +1025,59 @@ export class QaSessionController {
975
1025
  });
976
1026
  return attempt;
977
1027
  }
1028
+ /** Branch a completed answer without submitting or cancelling a turn. */
1029
+ async forkFromAnswer(answerSeq) {
1030
+ const parent = this.session;
1031
+ const remote = this.forkSessionRemote;
1032
+ if (this.disposed ||
1033
+ this.forking ||
1034
+ remote === undefined ||
1035
+ parent === undefined ||
1036
+ this.viewingSubagent !== null ||
1037
+ this.compatibilityReadOnly ||
1038
+ !this.policyReady ||
1039
+ this.config.session.policy === "fixed")
1040
+ return;
1041
+ const operation = this.generation;
1042
+ this.forking = true;
1043
+ try {
1044
+ const result = await remote(this.accounts?.token() ?? "", String(parent.sessionId), answerSeq);
1045
+ if (!result.ok)
1046
+ throw new Error("The Host refused to create the branch.");
1047
+ this.accounts?.onSessionCreated(result.value);
1048
+ this.chats.addChat(result.value);
1049
+ if (this.disposed ||
1050
+ operation !== this.generation ||
1051
+ parent !== this.session)
1052
+ return;
1053
+ ++this.generation;
1054
+ this.drafting = false;
1055
+ this.openChat();
1056
+ this.unbind();
1057
+ this.operationError = null;
1058
+ this.state = {
1059
+ ...QA_SESSION_IDLE_STATE,
1060
+ chatKey: this.chatKey,
1061
+ chatsRevision: this.chatsRevision,
1062
+ phase: "creating",
1063
+ };
1064
+ this.publish();
1065
+ const adoption = this.generation;
1066
+ if (await this.bind(result.value, { operation: adoption }))
1067
+ this.chats.saveActive(result.value);
1068
+ }
1069
+ catch (error) {
1070
+ if (!this.disposed &&
1071
+ (parent === this.session || this.session === undefined)) {
1072
+ this.operationError = "Не удалось создать ветку чата.";
1073
+ console.error("dsh-qa-surface: session operation failed (fork)", error);
1074
+ this.publish();
1075
+ }
1076
+ }
1077
+ finally {
1078
+ this.forking = false;
1079
+ }
1080
+ }
978
1081
  /**
979
1082
  * Open one of this browser's indexed chats. The id must be present in the
980
1083
  * host session list and pass policy attestation; an unknown id is forgotten
@@ -993,6 +1096,7 @@ export class QaSessionController {
993
1096
  this.unbind();
994
1097
  this.operationError = null;
995
1098
  this.policyReady = false;
1099
+ this.inheritedThroughSeq = undefined;
996
1100
  this.state = {
997
1101
  ...QA_SESSION_IDLE_STATE,
998
1102
  chatKey: this.chatKey,
@@ -1060,6 +1164,7 @@ export class QaSessionController {
1060
1164
  this.unbind();
1061
1165
  this.operationError = null;
1062
1166
  this.policyReady = false;
1167
+ this.inheritedThroughSeq = undefined;
1063
1168
  this.state = {
1064
1169
  ...QA_SESSION_IDLE_STATE,
1065
1170
  chatKey: this.chatKey,
@@ -1176,6 +1281,7 @@ export class QaSessionController {
1176
1281
  if (this.disposed)
1177
1282
  return;
1178
1283
  this.disposed = true;
1284
+ clearInterval(this.accessPoll);
1179
1285
  this.generation += 1;
1180
1286
  this.streamPublisher.clear();
1181
1287
  this.unsubscribeConnection();
@@ -1334,6 +1440,7 @@ export class QaSessionController {
1334
1440
  const operation = ++this.generation;
1335
1441
  this.unbind();
1336
1442
  this.policyReady = false;
1443
+ this.inheritedThroughSeq = undefined;
1337
1444
  this.state = { ...this.state, phase: "creating" };
1338
1445
  this.emit();
1339
1446
  try {
@@ -1505,6 +1612,7 @@ export class QaSessionController {
1505
1612
  this.session = binding.session;
1506
1613
  this.conversationBinding = this.conversation.binding(id);
1507
1614
  this.policyReady = false;
1615
+ this.inheritedThroughSeq = undefined;
1508
1616
  this.compatibilityReadOnly = false;
1509
1617
  this.unsubscribeSession = binding.session.subscribe(() => {
1510
1618
  this.admissionPending = false;
@@ -1733,10 +1841,13 @@ export class QaSessionController {
1733
1841
  // answers for its own sends.
1734
1842
  this.requestQueueNotice = null;
1735
1843
  this.policyReady = false;
1844
+ this.inheritedThroughSeq = undefined;
1736
1845
  this.compatibilityReadOnly = false;
1737
1846
  // The catalog belongs to the chat that produced it: another chat has a
1738
1847
  // different cwd, composition and role, so carrying it across would offer
1739
1848
  // actions this chat may not have.
1849
+ this.slashLoading = undefined;
1850
+ this.slashReadRevision += 1;
1740
1851
  this.slashSessionId = null;
1741
1852
  this.slashEntries = [];
1742
1853
  this.slashDenied = [];
@@ -1790,6 +1901,7 @@ export class QaSessionController {
1790
1901
  canSend: connected && !materializing,
1791
1902
  pendingMessage: this.pendingSubmission?.message ?? null,
1792
1903
  chatsRevision: this.chatsRevision,
1904
+ slash: this.slashView(),
1793
1905
  };
1794
1906
  this.emit();
1795
1907
  return;
@@ -1807,7 +1919,7 @@ export class QaSessionController {
1807
1919
  const snapshot = this.session.getSnapshot();
1808
1920
  const sessionId = String(this.session.sessionId);
1809
1921
  const conversationSnapshot = this.conversationBinding?.snapshot.getSnapshot();
1810
- const projectedSourceBundles = projectTurnSources(conversationSnapshot, sessionId, this.sourceAnchor(sessionId));
1922
+ const projectedSourceBundles = projectTurnSources(conversationSnapshot, sessionId, this.sourceAnchor(sessionId), this.config.tools.docsRoot);
1811
1923
  const sourceBundles = this.hostSources.merge(projectedSourceBundles);
1812
1924
  // A delegated child is not attested, so every Host RPC that admits the
1813
1925
  // session refuses it — and its evidence reaches the chat through the
@@ -1826,6 +1938,7 @@ export class QaSessionController {
1826
1938
  chatKey: this.chatKey,
1827
1939
  sessionSnapshot: snapshot,
1828
1940
  conversationSnapshot,
1941
+ inheritedThroughSeq: this.inheritedThroughSeq,
1829
1942
  sourceBundles,
1830
1943
  // The same directory the sources are anchored on: an answer that quotes a
1831
1944
  // file the turn made is masked to the name inside this workspace.
@@ -1875,6 +1988,26 @@ export class QaSessionController {
1875
1988
  }
1876
1989
  this.state = {
1877
1990
  ...projected,
1991
+ chatAccess: this.chatAccess?.sessionId === sessionId ? this.chatAccess : undefined,
1992
+ ...(this.chatAccess?.sessionId === sessionId &&
1993
+ this.chatAccess.mode !== "owner"
1994
+ ? {
1995
+ canSend: projected.canSend && this.chatAccess.mode === "collaborate",
1996
+ canStop: false,
1997
+ canEditQueue: false,
1998
+ approvals: [],
1999
+ questions: [],
2000
+ messages: projected.messages.map((message) => message.role === "assistant"
2001
+ ? {
2002
+ ...message,
2003
+ artifacts: message.artifacts?.map((artifact) => {
2004
+ const { workflow: _workflow, ...file } = artifact;
2005
+ return file;
2006
+ }),
2007
+ }
2008
+ : message),
2009
+ }
2010
+ : {}),
1878
2011
  pendingMessage: this.pendingSubmission?.message ?? null,
1879
2012
  };
1880
2013
  this.emit();
@@ -2019,7 +2152,9 @@ export class QaSessionController {
2019
2152
  if (session === undefined)
2020
2153
  return { kind: "refused", reason: null };
2021
2154
  const sessionId = String(session.sessionId);
2022
- if (!this.config.lockdown.enabled && this.accounts === undefined) {
2155
+ if (!this.config.lockdown.enabled &&
2156
+ this.accounts === undefined &&
2157
+ this.forkSessionRemote === undefined) {
2023
2158
  this.policyReady = true;
2024
2159
  return { kind: "ok", sessionId };
2025
2160
  }
@@ -2028,6 +2163,7 @@ export class QaSessionController {
2028
2163
  // lockdown, and skipping the call is what let a browser that restored
2029
2164
  // another account's chat write into it without ever being asked who spoke.
2030
2165
  this.policyReady = false;
2166
+ this.inheritedThroughSeq = undefined;
2031
2167
  this.publish();
2032
2168
  const outcome = await attestQaPolicy({
2033
2169
  secureSession: this.secureSessionRemote,
@@ -2039,6 +2175,8 @@ export class QaSessionController {
2039
2175
  if (this.session !== session)
2040
2176
  return { kind: "stale" };
2041
2177
  if (outcome.ok) {
2178
+ this.chatAccess = outcome.chatAccess;
2179
+ this.inheritedThroughSeq = outcome.inheritedThroughSeq;
2042
2180
  this.policyReady = true;
2043
2181
  this.operationError = null;
2044
2182
  this.publish();
@@ -2051,6 +2189,7 @@ export class QaSessionController {
2051
2189
  return { kind: "refused", reason: outcome.reason };
2052
2190
  }
2053
2191
  this.policyReady = false;
2192
+ this.inheritedThroughSeq = undefined;
2054
2193
  this.operationError = CONFIGURATION_ERROR;
2055
2194
  this.publish();
2056
2195
  return { kind: "refused", reason: outcome.reason };
@@ -2059,6 +2198,43 @@ export class QaSessionController {
2059
2198
  for (const listener of this.listeners)
2060
2199
  listener();
2061
2200
  }
2201
+ async refreshChatAccess() {
2202
+ const session = this.session;
2203
+ const token = this.accounts?.token();
2204
+ if (session === undefined ||
2205
+ token == null ||
2206
+ this.sharingApi === undefined ||
2207
+ this.disposed)
2208
+ return;
2209
+ // Delegated sessions have no independent QA membership. Their transcript
2210
+ // transport checks authoritative lineage; the periodic account check must
2211
+ // keep checking the owning chat, including while a child is on screen.
2212
+ const delegated = this.viewingSubagent !== null;
2213
+ const id = delegated ? this.chatSessionId : String(session.sessionId);
2214
+ if (id === null)
2215
+ return;
2216
+ const operation = this.generation;
2217
+ try {
2218
+ const result = await this.sharingApi.access(token, id);
2219
+ if (this.session !== session ||
2220
+ this.disposed ||
2221
+ operation !== this.generation)
2222
+ return;
2223
+ if (!result.ok || (delegated && result.value.mode !== "owner")) {
2224
+ this.unbind();
2225
+ this.chatAccess = undefined;
2226
+ this.fail("Доступ к чату отозван или аккаунт недоступен.", new QaPolicyAttestationError("session-owned-elsewhere"));
2227
+ return;
2228
+ }
2229
+ if (JSON.stringify(this.chatAccess) !== JSON.stringify(result.value)) {
2230
+ this.chatAccess = result.value;
2231
+ this.publish();
2232
+ }
2233
+ }
2234
+ catch {
2235
+ /* Transport failures do not fabricate a revocation. Server writes still recheck access. */
2236
+ }
2237
+ }
2062
2238
  /**
2063
2239
  * Answer a request the Host parked for this chat. The Host re-checks the
2064
2240
  * request against the chat, so an answer that lost its race is a no-op; the