@stigmer/react 3.1.1 → 3.1.3

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 (483) hide show
  1. package/composer/SessionComposer.d.ts +25 -1
  2. package/composer/SessionComposer.d.ts.map +1 -1
  3. package/composer/SessionComposer.js +19 -8
  4. package/composer/SessionComposer.js.map +1 -1
  5. package/execution/ArtifactContentRenderer.d.ts +11 -1
  6. package/execution/ArtifactContentRenderer.d.ts.map +1 -1
  7. package/execution/ArtifactContentRenderer.js +39 -15
  8. package/execution/ArtifactContentRenderer.js.map +1 -1
  9. package/execution/ArtifactPreviewModal.d.ts +5 -5
  10. package/execution/ArtifactPreviewModal.js +4 -4
  11. package/execution/ArtifactPreviewModal.js.map +1 -1
  12. package/execution/FileChangeProgressBar.d.ts +47 -0
  13. package/execution/FileChangeProgressBar.d.ts.map +1 -0
  14. package/execution/FileChangeProgressBar.js +49 -0
  15. package/execution/FileChangeProgressBar.js.map +1 -0
  16. package/execution/FileChangesView.d.ts.map +1 -1
  17. package/execution/FileChangesView.js +19 -4
  18. package/execution/FileChangesView.js.map +1 -1
  19. package/execution/FilePathContext.d.ts +8 -1
  20. package/execution/FilePathContext.d.ts.map +1 -1
  21. package/execution/FilePathContext.js.map +1 -1
  22. package/execution/FilePathLink.d.ts.map +1 -1
  23. package/execution/FilePathLink.js +21 -6
  24. package/execution/FilePathLink.js.map +1 -1
  25. package/execution/FileReviewAtoms.d.ts +27 -0
  26. package/execution/FileReviewAtoms.d.ts.map +1 -0
  27. package/execution/FileReviewAtoms.js +64 -0
  28. package/execution/FileReviewAtoms.js.map +1 -0
  29. package/execution/FileReviewCard.d.ts +2 -2
  30. package/execution/FileReviewCard.d.ts.map +1 -1
  31. package/execution/FileReviewCard.js +32 -62
  32. package/execution/FileReviewCard.js.map +1 -1
  33. package/execution/MessageEntry.d.ts +20 -0
  34. package/execution/MessageEntry.d.ts.map +1 -1
  35. package/execution/MessageEntry.js +19 -5
  36. package/execution/MessageEntry.js.map +1 -1
  37. package/execution/MessageThread.d.ts +75 -13
  38. package/execution/MessageThread.d.ts.map +1 -1
  39. package/execution/MessageThread.js +195 -27
  40. package/execution/MessageThread.js.map +1 -1
  41. package/execution/PlanArtifactCard.d.ts +36 -21
  42. package/execution/PlanArtifactCard.d.ts.map +1 -1
  43. package/execution/PlanArtifactCard.js +39 -25
  44. package/execution/PlanArtifactCard.js.map +1 -1
  45. package/execution/PlanCompletionCard.d.ts +4 -4
  46. package/execution/PlanCompletionCard.d.ts.map +1 -1
  47. package/execution/PlanCompletionCard.js +4 -4
  48. package/execution/PlanCompletionCard.js.map +1 -1
  49. package/execution/PlanDocumentMessage.d.ts +32 -0
  50. package/execution/PlanDocumentMessage.d.ts.map +1 -0
  51. package/execution/PlanDocumentMessage.js +37 -0
  52. package/execution/PlanDocumentMessage.js.map +1 -0
  53. package/execution/PlanStreamingCard.d.ts +50 -0
  54. package/execution/PlanStreamingCard.d.ts.map +1 -0
  55. package/execution/PlanStreamingCard.js +40 -0
  56. package/execution/PlanStreamingCard.js.map +1 -0
  57. package/execution/index.d.ts +9 -1
  58. package/execution/index.d.ts.map +1 -1
  59. package/execution/index.js +4 -0
  60. package/execution/index.js.map +1 -1
  61. package/execution/use-build-from-plan-hotkey.d.ts +1 -1
  62. package/execution/use-build-from-plan-hotkey.js +1 -1
  63. package/execution/useArtifactCopy.d.ts +51 -0
  64. package/execution/useArtifactCopy.d.ts.map +1 -0
  65. package/execution/useArtifactCopy.js +75 -0
  66. package/execution/useArtifactCopy.js.map +1 -0
  67. package/execution/useArtifactDownload.d.ts.map +1 -1
  68. package/execution/useArtifactDownload.js +17 -5
  69. package/execution/useArtifactDownload.js.map +1 -1
  70. package/execution/useCreateAgentExecution.d.ts +12 -0
  71. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  72. package/execution/useCreateAgentExecution.js +5 -1
  73. package/execution/useCreateAgentExecution.js.map +1 -1
  74. package/github/decodeGitHubContent.d.ts +29 -0
  75. package/github/decodeGitHubContent.d.ts.map +1 -0
  76. package/github/decodeGitHubContent.js +72 -0
  77. package/github/decodeGitHubContent.js.map +1 -0
  78. package/github/index.d.ts +1 -0
  79. package/github/index.d.ts.map +1 -1
  80. package/github/index.js +1 -0
  81. package/github/index.js.map +1 -1
  82. package/github/useGitHubFileReader.d.ts +26 -0
  83. package/github/useGitHubFileReader.d.ts.map +1 -0
  84. package/github/useGitHubFileReader.js +106 -0
  85. package/github/useGitHubFileReader.js.map +1 -0
  86. package/github/useGitHubTreeLister.d.ts.map +1 -1
  87. package/github/useGitHubTreeLister.js +6 -2
  88. package/github/useGitHubTreeLister.js.map +1 -1
  89. package/index.d.ts +9 -9
  90. package/index.d.ts.map +1 -1
  91. package/index.js +5 -5
  92. package/index.js.map +1 -1
  93. package/internal/ResizableSplit.d.ts +72 -22
  94. package/internal/ResizableSplit.d.ts.map +1 -1
  95. package/internal/ResizableSplit.js +96 -38
  96. package/internal/ResizableSplit.js.map +1 -1
  97. package/internal/VirtualizedThread.d.ts +5 -3
  98. package/internal/VirtualizedThread.d.ts.map +1 -1
  99. package/internal/VirtualizedThread.js +6 -4
  100. package/internal/VirtualizedThread.js.map +1 -1
  101. package/internal/file-icons/index.d.ts +17 -0
  102. package/internal/file-icons/index.d.ts.map +1 -0
  103. package/internal/file-icons/index.js +60 -0
  104. package/internal/file-icons/index.js.map +1 -0
  105. package/internal/file-tree/FileTreeNode.d.ts +18 -1
  106. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  107. package/internal/file-tree/FileTreeNode.js +11 -2
  108. package/internal/file-tree/FileTreeNode.js.map +1 -1
  109. package/internal/markdown-components.d.ts +38 -1
  110. package/internal/markdown-components.d.ts.map +1 -1
  111. package/internal/markdown-components.js +86 -11
  112. package/internal/markdown-components.js.map +1 -1
  113. package/{workflow → internal}/motion-preference.d.ts +7 -4
  114. package/internal/motion-preference.d.ts.map +1 -0
  115. package/{workflow → internal}/motion-preference.js +7 -4
  116. package/internal/motion-preference.js.map +1 -0
  117. package/internal/store/index.d.ts +33 -0
  118. package/internal/store/index.d.ts.map +1 -1
  119. package/internal/store/index.js +59 -0
  120. package/internal/store/index.js.map +1 -1
  121. package/internal/store/workspace-editors-store.d.ts +145 -0
  122. package/internal/store/workspace-editors-store.d.ts.map +1 -0
  123. package/internal/store/workspace-editors-store.js +208 -0
  124. package/internal/store/workspace-editors-store.js.map +1 -0
  125. package/internal/store/workspace-file-selection-store.d.ts +38 -0
  126. package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
  127. package/internal/store/workspace-file-selection-store.js +62 -0
  128. package/internal/store/workspace-file-selection-store.js.map +1 -0
  129. package/internal/useRevealLine.d.ts +49 -0
  130. package/internal/useRevealLine.d.ts.map +1 -0
  131. package/internal/useRevealLine.js +43 -0
  132. package/internal/useRevealLine.js.map +1 -0
  133. package/library/detect-plan-artifact.d.ts +43 -4
  134. package/library/detect-plan-artifact.d.ts.map +1 -1
  135. package/library/detect-plan-artifact.js +45 -5
  136. package/library/detect-plan-artifact.js.map +1 -1
  137. package/library/detect-streaming-plan.d.ts +45 -0
  138. package/library/detect-streaming-plan.d.ts.map +1 -0
  139. package/library/detect-streaming-plan.js +83 -0
  140. package/library/detect-streaming-plan.js.map +1 -0
  141. package/library/index.d.ts +4 -1
  142. package/library/index.d.ts.map +1 -1
  143. package/library/index.js +2 -1
  144. package/library/index.js.map +1 -1
  145. package/package.json +4 -4
  146. package/session/NewSessionViewer.d.ts +19 -4
  147. package/session/NewSessionViewer.d.ts.map +1 -1
  148. package/session/NewSessionViewer.js +52 -25
  149. package/session/NewSessionViewer.js.map +1 -1
  150. package/session/PlanEditor.d.ts +55 -0
  151. package/session/PlanEditor.d.ts.map +1 -0
  152. package/session/PlanEditor.js +88 -0
  153. package/session/PlanEditor.js.map +1 -0
  154. package/session/PlanStreamingDocument.d.ts +30 -0
  155. package/session/PlanStreamingDocument.d.ts.map +1 -0
  156. package/session/PlanStreamingDocument.js +37 -0
  157. package/session/PlanStreamingDocument.js.map +1 -0
  158. package/session/SessionPanelChip.d.ts +26 -0
  159. package/session/SessionPanelChip.d.ts.map +1 -0
  160. package/session/SessionPanelChip.js +40 -0
  161. package/session/SessionPanelChip.js.map +1 -0
  162. package/session/SessionViewer.d.ts +34 -7
  163. package/session/SessionViewer.d.ts.map +1 -1
  164. package/session/SessionViewer.js +334 -33
  165. package/session/SessionViewer.js.map +1 -1
  166. package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
  167. package/session/facets/ArtifactsTab.d.ts.map +1 -0
  168. package/session/{inspector → facets}/ArtifactsTab.js +8 -3
  169. package/session/facets/ArtifactsTab.js.map +1 -0
  170. package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
  171. package/session/facets/ChangesTab.d.ts.map +1 -0
  172. package/session/{inspector → facets}/ChangesTab.js +3 -3
  173. package/session/facets/ChangesTab.js.map +1 -0
  174. package/session/facets/InspectTab.d.ts.map +1 -0
  175. package/session/facets/InspectTab.js.map +1 -0
  176. package/session/{inspector → facets}/SetupTab.d.ts +11 -2
  177. package/session/facets/SetupTab.d.ts.map +1 -0
  178. package/session/{inspector → facets}/SetupTab.js +4 -3
  179. package/session/facets/SetupTab.js.map +1 -0
  180. package/session/{inspector → facets}/UsageTab.d.ts +1 -1
  181. package/session/facets/UsageTab.d.ts.map +1 -0
  182. package/session/{inspector → facets}/UsageTab.js +1 -1
  183. package/session/facets/UsageTab.js.map +1 -0
  184. package/session/{inspector → facets}/index.d.ts +0 -6
  185. package/session/facets/index.d.ts.map +1 -0
  186. package/session/{inspector → facets}/index.js +0 -3
  187. package/session/facets/index.js.map +1 -0
  188. package/session/index.d.ts +11 -2
  189. package/session/index.d.ts.map +1 -1
  190. package/session/index.js +15 -2
  191. package/session/index.js.map +1 -1
  192. package/session/plan-document.d.ts +21 -0
  193. package/session/plan-document.d.ts.map +1 -0
  194. package/session/plan-document.js +28 -0
  195. package/session/plan-document.js.map +1 -0
  196. package/session/useOpenFileChange.d.ts +20 -0
  197. package/session/useOpenFileChange.d.ts.map +1 -0
  198. package/session/useOpenFileChange.js +31 -0
  199. package/session/useOpenFileChange.js.map +1 -0
  200. package/session/usePlanDraft.d.ts +52 -0
  201. package/session/usePlanDraft.d.ts.map +1 -0
  202. package/session/usePlanDraft.js +63 -0
  203. package/session/usePlanDraft.js.map +1 -0
  204. package/session/useSessionConversation.d.ts +14 -1
  205. package/session/useSessionConversation.d.ts.map +1 -1
  206. package/session/useSessionConversation.js +20 -2
  207. package/session/useSessionConversation.js.map +1 -1
  208. package/session/useSessionPageFlow.d.ts.map +1 -1
  209. package/session/useSessionPageFlow.js +1 -0
  210. package/session/useSessionPageFlow.js.map +1 -1
  211. package/session/useSessionPanel.d.ts +120 -0
  212. package/session/useSessionPanel.d.ts.map +1 -0
  213. package/session/useSessionPanel.js +130 -0
  214. package/session/useSessionPanel.js.map +1 -0
  215. package/session/useSessionRailViews.d.ts +45 -0
  216. package/session/useSessionRailViews.d.ts.map +1 -0
  217. package/session/useSessionRailViews.js +105 -0
  218. package/session/useSessionRailViews.js.map +1 -0
  219. package/src/composer/SessionComposer.tsx +53 -10
  220. package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
  221. package/src/execution/ArtifactContentRenderer.tsx +101 -24
  222. package/src/execution/ArtifactPreviewModal.tsx +9 -9
  223. package/src/execution/FileChangeProgressBar.tsx +121 -0
  224. package/src/execution/FileChangesView.tsx +18 -4
  225. package/src/execution/FilePathContext.tsx +8 -1
  226. package/src/execution/FilePathLink.tsx +22 -6
  227. package/src/execution/FileReviewAtoms.tsx +89 -0
  228. package/src/execution/FileReviewCard.tsx +57 -90
  229. package/src/execution/MessageEntry.tsx +49 -1
  230. package/src/execution/MessageThread.tsx +308 -39
  231. package/src/execution/PlanArtifactCard.tsx +174 -64
  232. package/src/execution/PlanCompletionCard.tsx +5 -5
  233. package/src/execution/PlanDocumentMessage.tsx +96 -0
  234. package/src/execution/PlanStreamingCard.tsx +141 -0
  235. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
  236. package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
  237. package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
  238. package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
  239. package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
  240. package/src/execution/__tests__/MessageThread.test.tsx +102 -1
  241. package/src/execution/__tests__/PlanArtifactCard.test.tsx +114 -26
  242. package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
  243. package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
  244. package/src/execution/__tests__/PlanStreamingCard.test.tsx +56 -0
  245. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +600 -22
  246. package/src/execution/__tests__/message-entry.test.tsx +17 -0
  247. package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
  248. package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
  249. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
  250. package/src/execution/index.ts +13 -1
  251. package/src/execution/use-build-from-plan-hotkey.ts +1 -1
  252. package/src/execution/useArtifactCopy.ts +109 -0
  253. package/src/execution/useArtifactDownload.ts +17 -5
  254. package/src/execution/useCreateAgentExecution.ts +18 -1
  255. package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
  256. package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
  257. package/src/github/decodeGitHubContent.ts +83 -0
  258. package/src/github/index.ts +1 -0
  259. package/src/github/useGitHubFileReader.ts +152 -0
  260. package/src/github/useGitHubTreeLister.ts +6 -2
  261. package/src/index.ts +69 -9
  262. package/src/internal/ResizableSplit.tsx +184 -51
  263. package/src/internal/VirtualizedThread.tsx +12 -4
  264. package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
  265. package/src/internal/__tests__/markdown-components.test.tsx +68 -0
  266. package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
  267. package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
  268. package/src/internal/file-icons/index.tsx +137 -0
  269. package/src/internal/file-tree/FileTreeNode.tsx +56 -5
  270. package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
  271. package/src/internal/markdown-components.tsx +116 -11
  272. package/src/{workflow → internal}/motion-preference.ts +7 -4
  273. package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
  274. package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
  275. package/src/internal/store/index.ts +94 -0
  276. package/src/internal/store/workspace-editors-store.ts +315 -0
  277. package/src/internal/store/workspace-file-selection-store.ts +88 -0
  278. package/src/internal/useRevealLine.ts +81 -0
  279. package/src/library/__tests__/detect-plan-artifact.test.ts +69 -0
  280. package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
  281. package/src/library/detect-plan-artifact.ts +62 -5
  282. package/src/library/detect-streaming-plan.ts +108 -0
  283. package/src/library/index.ts +7 -0
  284. package/src/session/NewSessionViewer.tsx +123 -55
  285. package/src/session/PlanEditor.tsx +361 -0
  286. package/src/session/PlanStreamingDocument.tsx +97 -0
  287. package/src/session/SessionPanelChip.tsx +103 -0
  288. package/src/session/SessionViewer.tsx +655 -96
  289. package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
  290. package/src/session/__tests__/PlanEditor.test.tsx +217 -0
  291. package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
  292. package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
  293. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +66 -4
  294. package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
  295. package/src/session/__tests__/audienceWiring.test.tsx +55 -19
  296. package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
  297. package/src/session/__tests__/usePlanDraft.test.ts +102 -0
  298. package/src/session/__tests__/useSessionConversation.test.tsx +44 -0
  299. package/src/session/__tests__/useSessionPanel.test.ts +298 -0
  300. package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
  301. package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
  302. package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
  303. package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
  304. package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
  305. package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +42 -8
  306. package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
  307. package/src/session/{inspector → facets}/index.ts +0 -16
  308. package/src/session/index.ts +30 -13
  309. package/src/session/plan-document.ts +30 -0
  310. package/src/session/useOpenFileChange.ts +53 -0
  311. package/src/session/usePlanDraft.ts +109 -0
  312. package/src/session/useSessionConversation.ts +38 -3
  313. package/src/session/useSessionPageFlow.ts +1 -0
  314. package/src/session/useSessionPanel.ts +288 -0
  315. package/src/session/useSessionRailViews.tsx +200 -0
  316. package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
  317. package/src/workflow/index.ts +1 -1
  318. package/src/workflow/useFollowExecution.ts +1 -1
  319. package/src/workspace/EditorTabs.tsx +174 -0
  320. package/src/workspace/ExplorerTree.tsx +253 -0
  321. package/src/workspace/FileViewer.tsx +537 -0
  322. package/src/workspace/WorkspaceContentSearch.tsx +502 -0
  323. package/src/workspace/WorkspaceContentSearcher.ts +70 -0
  324. package/src/workspace/WorkspaceEditor.tsx +19 -16
  325. package/src/workspace/WorkspaceFileLister.ts +11 -0
  326. package/src/workspace/WorkspaceFileReader.ts +57 -0
  327. package/src/workspace/WorkspaceFileSearch.tsx +452 -0
  328. package/src/workspace/WorkspaceSurface.tsx +839 -0
  329. package/src/workspace/__tests__/EditorTabs.test.tsx +102 -0
  330. package/src/workspace/__tests__/ExplorerTree.test.tsx +204 -0
  331. package/src/workspace/__tests__/FileViewer.test.tsx +439 -0
  332. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +229 -0
  333. package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
  334. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +227 -0
  335. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +418 -0
  336. package/src/workspace/__tests__/a11y/harness.tsx +140 -0
  337. package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
  338. package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
  339. package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
  340. package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
  341. package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
  342. package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
  343. package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
  344. package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
  345. package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
  346. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
  347. package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
  348. package/src/workspace/findChangeForSelection.ts +61 -0
  349. package/src/workspace/index.ts +56 -2
  350. package/src/workspace/matchContentPreview.ts +51 -0
  351. package/src/workspace/matchWorkspaceFiles.ts +86 -0
  352. package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
  353. package/src/workspace/useWorkspaceContentSearch.ts +228 -0
  354. package/src/workspace/useWorkspaceFileContent.ts +129 -0
  355. package/src/workspace/useWorkspaceFileSearch.ts +184 -0
  356. package/src/workspace/useWorkspaceFiles.ts +27 -27
  357. package/src/workspace/workspaceListingCache.ts +93 -0
  358. package/styles.css +1 -1
  359. package/workflow/WorkflowExecutionGraph.js +1 -1
  360. package/workflow/WorkflowExecutionGraph.js.map +1 -1
  361. package/workflow/index.d.ts +1 -1
  362. package/workflow/index.d.ts.map +1 -1
  363. package/workflow/index.js +1 -1
  364. package/workflow/index.js.map +1 -1
  365. package/workflow/useFollowExecution.js +1 -1
  366. package/workflow/useFollowExecution.js.map +1 -1
  367. package/workspace/EditorTabs.d.ts +47 -0
  368. package/workspace/EditorTabs.d.ts.map +1 -0
  369. package/workspace/EditorTabs.js +78 -0
  370. package/workspace/EditorTabs.js.map +1 -0
  371. package/workspace/ExplorerTree.d.ts +32 -0
  372. package/workspace/ExplorerTree.d.ts.map +1 -0
  373. package/workspace/ExplorerTree.js +49 -0
  374. package/workspace/ExplorerTree.js.map +1 -0
  375. package/workspace/FileViewer.d.ts +93 -0
  376. package/workspace/FileViewer.d.ts.map +1 -0
  377. package/workspace/FileViewer.js +193 -0
  378. package/workspace/FileViewer.js.map +1 -0
  379. package/workspace/WorkspaceContentSearch.d.ts +41 -0
  380. package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
  381. package/workspace/WorkspaceContentSearch.js +216 -0
  382. package/workspace/WorkspaceContentSearch.js.map +1 -0
  383. package/workspace/WorkspaceContentSearcher.d.ts +59 -0
  384. package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
  385. package/workspace/WorkspaceContentSearcher.js +7 -0
  386. package/workspace/WorkspaceContentSearcher.js.map +1 -0
  387. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  388. package/workspace/WorkspaceEditor.js +8 -10
  389. package/workspace/WorkspaceEditor.js.map +1 -1
  390. package/workspace/WorkspaceFileLister.d.ts +11 -0
  391. package/workspace/WorkspaceFileLister.d.ts.map +1 -1
  392. package/workspace/WorkspaceFileReader.d.ts +48 -0
  393. package/workspace/WorkspaceFileReader.d.ts.map +1 -0
  394. package/workspace/WorkspaceFileReader.js +11 -0
  395. package/workspace/WorkspaceFileReader.js.map +1 -0
  396. package/workspace/WorkspaceFileSearch.d.ts +32 -0
  397. package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
  398. package/workspace/WorkspaceFileSearch.js +175 -0
  399. package/workspace/WorkspaceFileSearch.js.map +1 -0
  400. package/workspace/WorkspaceSurface.d.ts +151 -0
  401. package/workspace/WorkspaceSurface.d.ts.map +1 -0
  402. package/workspace/WorkspaceSurface.js +220 -0
  403. package/workspace/WorkspaceSurface.js.map +1 -0
  404. package/workspace/findChangeForSelection.d.ts +19 -0
  405. package/workspace/findChangeForSelection.d.ts.map +1 -0
  406. package/workspace/findChangeForSelection.js +46 -0
  407. package/workspace/findChangeForSelection.js.map +1 -0
  408. package/workspace/index.d.ts +24 -2
  409. package/workspace/index.d.ts.map +1 -1
  410. package/workspace/index.js +13 -1
  411. package/workspace/index.js.map +1 -1
  412. package/workspace/matchContentPreview.d.ts +21 -0
  413. package/workspace/matchContentPreview.d.ts.map +1 -0
  414. package/workspace/matchContentPreview.js +36 -0
  415. package/workspace/matchContentPreview.js.map +1 -0
  416. package/workspace/matchWorkspaceFiles.d.ts +30 -0
  417. package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
  418. package/workspace/matchWorkspaceFiles.js +58 -0
  419. package/workspace/matchWorkspaceFiles.js.map +1 -0
  420. package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
  421. package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
  422. package/workspace/resolveWorkspaceFileSelection.js +115 -0
  423. package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
  424. package/workspace/useWorkspaceContentSearch.d.ts +69 -0
  425. package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
  426. package/workspace/useWorkspaceContentSearch.js +154 -0
  427. package/workspace/useWorkspaceContentSearch.js.map +1 -0
  428. package/workspace/useWorkspaceFileContent.d.ts +73 -0
  429. package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
  430. package/workspace/useWorkspaceFileContent.js +57 -0
  431. package/workspace/useWorkspaceFileContent.js.map +1 -0
  432. package/workspace/useWorkspaceFileSearch.d.ts +61 -0
  433. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
  434. package/workspace/useWorkspaceFileSearch.js +119 -0
  435. package/workspace/useWorkspaceFileSearch.js.map +1 -0
  436. package/workspace/useWorkspaceFiles.d.ts +13 -5
  437. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  438. package/workspace/useWorkspaceFiles.js +18 -19
  439. package/workspace/useWorkspaceFiles.js.map +1 -1
  440. package/workspace/workspaceListingCache.d.ts +47 -0
  441. package/workspace/workspaceListingCache.d.ts.map +1 -0
  442. package/workspace/workspaceListingCache.js +55 -0
  443. package/workspace/workspaceListingCache.js.map +1 -0
  444. package/session/inspector/ArtifactsTab.d.ts.map +0 -1
  445. package/session/inspector/ArtifactsTab.js.map +0 -1
  446. package/session/inspector/ChangesTab.d.ts.map +0 -1
  447. package/session/inspector/ChangesTab.js.map +0 -1
  448. package/session/inspector/InspectTab.d.ts.map +0 -1
  449. package/session/inspector/InspectTab.js.map +0 -1
  450. package/session/inspector/SessionInspector.d.ts +0 -64
  451. package/session/inspector/SessionInspector.d.ts.map +0 -1
  452. package/session/inspector/SessionInspector.js +0 -53
  453. package/session/inspector/SessionInspector.js.map +0 -1
  454. package/session/inspector/SetupTab.d.ts.map +0 -1
  455. package/session/inspector/SetupTab.js.map +0 -1
  456. package/session/inspector/UsageTab.d.ts.map +0 -1
  457. package/session/inspector/UsageTab.js.map +0 -1
  458. package/session/inspector/WorkspaceTab.d.ts +0 -35
  459. package/session/inspector/WorkspaceTab.d.ts.map +0 -1
  460. package/session/inspector/WorkspaceTab.js +0 -75
  461. package/session/inspector/WorkspaceTab.js.map +0 -1
  462. package/session/inspector/index.d.ts.map +0 -1
  463. package/session/inspector/index.js.map +0 -1
  464. package/session/inspector/useSessionInspector.d.ts +0 -49
  465. package/session/inspector/useSessionInspector.d.ts.map +0 -1
  466. package/session/inspector/useSessionInspector.js +0 -122
  467. package/session/inspector/useSessionInspector.js.map +0 -1
  468. package/src/session/inspector/SessionInspector.tsx +0 -162
  469. package/src/session/inspector/WorkspaceTab.tsx +0 -245
  470. package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
  471. package/src/session/inspector/useSessionInspector.ts +0 -178
  472. package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
  473. package/workflow/motion-preference.d.ts.map +0 -1
  474. package/workflow/motion-preference.js.map +0 -1
  475. package/workspace/WorkspaceEntryFiles.d.ts +0 -22
  476. package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
  477. package/workspace/WorkspaceEntryFiles.js +0 -58
  478. package/workspace/WorkspaceEntryFiles.js.map +0 -1
  479. /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
  480. /package/session/{inspector → facets}/InspectTab.js +0 -0
  481. /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
  482. /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
  483. /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
@@ -0,0 +1,205 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { render, screen, cleanup, act, fireEvent } from "@testing-library/react";
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Wiring-contract test for transcript click-to-open: the onFilePathClick
6
+ // SessionViewer hands to MessageThread resolves a tool-call path, writes the
7
+ // open-editor store, AND expands the session panel — so the resolved file
8
+ // surfaces in the WorkspaceSurface. MessageThread and WorkspaceSurface render
9
+ // as prop-capturing probes; the store + SessionPanelRegion wire between them
10
+ // is the subject under test.
11
+ // ---------------------------------------------------------------------------
12
+
13
+ type CapturedProps = Record<string, unknown>;
14
+
15
+ const threadProps: CapturedProps[] = [];
16
+ vi.mock("../../execution/MessageThread", () => ({
17
+ MessageThread: (props: CapturedProps) => {
18
+ threadProps.push(props);
19
+ return <div data-testid="thread-probe" />;
20
+ },
21
+ }));
22
+
23
+ const surfaceProps: CapturedProps[] = [];
24
+ vi.mock("../../workspace/WorkspaceSurface", () => ({
25
+ WorkspaceSurface: (props: CapturedProps) => {
26
+ surfaceProps.push(props);
27
+ return <div data-testid="surface-probe" />;
28
+ },
29
+ }));
30
+
31
+ vi.mock("../../composer", async (importOriginal) => {
32
+ const actual = await importOriginal<typeof import("../../composer")>();
33
+ return {
34
+ ...actual,
35
+ SessionComposer: () => <div data-testid="composer-probe" />,
36
+ };
37
+ });
38
+
39
+ const gitEntry = {
40
+ id: "e1",
41
+ name: "acme/app",
42
+ type: "git" as const,
43
+ gitUrl: "https://github.com/acme/app.git",
44
+ gitBranch: "main",
45
+ };
46
+
47
+ const stubWorkspace = {
48
+ entries: [gitEntry],
49
+ hasEntries: true,
50
+ toInput: vi.fn().mockReturnValue([]),
51
+ addGitRepo: vi.fn(),
52
+ addLocalPath: vi.fn(),
53
+ remove: vi.fn(),
54
+ clear: vi.fn(),
55
+ clearLocal: vi.fn(),
56
+ };
57
+
58
+ const stubConv = {
59
+ session: { spec: {} },
60
+ isLoading: false,
61
+ loadError: null,
62
+ completedExecutions: [],
63
+ activeStreamExecution: null,
64
+ pendingUserMessage: null,
65
+ workspaceEntries: [],
66
+ fileChangeSets: [],
67
+ submitFileDecision: vi.fn(),
68
+ submittingFileDecisionKeys: new Set<string>(),
69
+ fileDecisionErrors: new Map<string, Error>(),
70
+ sendError: null,
71
+ stopError: null,
72
+ streamError: null,
73
+ reconnectStream: vi.fn(),
74
+ submittingApprovalIds: new Set<string>(),
75
+ approvalErrors: new Map<string, Error>(),
76
+ isStoppable: false,
77
+ isStopping: false,
78
+ isSending: false,
79
+ canSendFollowUp: true,
80
+ isReconnecting: false,
81
+ connectTimedOut: false,
82
+ isSlow: false,
83
+ stop: vi.fn(),
84
+ retryLastSend: vi.fn(),
85
+ };
86
+
87
+ const stubSessionPageFlow = {
88
+ conv: stubConv,
89
+ harness: "native" as const,
90
+ executionTarget: undefined,
91
+ model: [undefined, vi.fn()] as const,
92
+ interactionMode: ["agent" as const, vi.fn()] as const,
93
+ agentRef: { org: "acme", slug: "support-bot" },
94
+ setAgentRef: vi.fn(),
95
+ resolution: null,
96
+ setResolution: vi.fn(),
97
+ isDefaultAgent: false,
98
+ mcpServerUsages: [],
99
+ setMcpServerUsages: vi.fn(),
100
+ skillRefs: [],
101
+ setSkillRefs: vi.fn(),
102
+ workspace: stubWorkspace,
103
+ sessionVariables: { entries: [], isEmpty: true, clear: vi.fn() },
104
+ autoApproveAll: false,
105
+ setAutoApproveAll: vi.fn(),
106
+ handleSubmit: vi.fn(),
107
+ submitError: null as Error | null,
108
+ displayExecution: null,
109
+ allExecutions: [],
110
+ sandboxWorkspaceRoot: "/home/daytona/workspace",
111
+ };
112
+ vi.mock("../useSessionPageFlow", () => ({
113
+ useSessionPageFlow: () => stubSessionPageFlow,
114
+ }));
115
+
116
+ vi.mock("../../hooks", () => ({
117
+ useStigmer: () => ({
118
+ agentExecution: {
119
+ uploadAttachment: vi.fn(),
120
+ getArtifactContent: vi.fn(),
121
+ },
122
+ }),
123
+ }));
124
+
125
+ import { SessionViewer } from "../SessionViewer";
126
+
127
+ /** The onFilePathClick handler threaded down to the (mocked) MessageThread. */
128
+ function capturedOnFilePathClick(): (path: string) => boolean {
129
+ const props = threadProps.at(-1);
130
+ return props?.onFilePathClick as (path: string) => boolean;
131
+ }
132
+
133
+ /** The latest selectedFile the (mocked) WorkspaceSurface received, if rendered. */
134
+ function latestSurfaceSelectedFile(): unknown {
135
+ return surfaceProps.at(-1)?.selectedFile ?? null;
136
+ }
137
+
138
+ beforeEach(() => {
139
+ threadProps.length = 0;
140
+ surfaceProps.length = 0;
141
+ });
142
+
143
+ afterEach(() => {
144
+ cleanup();
145
+ vi.clearAllMocks();
146
+ });
147
+
148
+ describe("SessionViewer — transcript click-to-open wiring", () => {
149
+ it("threads an onFilePathClick to the message thread", () => {
150
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
151
+ expect(typeof capturedOnFilePathClick()).toBe("function");
152
+ });
153
+
154
+ it("opens a resolvable tool-call path in the session panel", () => {
155
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
156
+ // Before any click: panel collapsed — the surface is not rendered.
157
+ expect(surfaceProps.length).toBe(0);
158
+
159
+ let handled: boolean | undefined;
160
+ act(() => {
161
+ handled = capturedOnFilePathClick()("/home/daytona/workspace/src/main.go");
162
+ });
163
+
164
+ expect(handled).toBe(true);
165
+ // Opening expands the panel and routes the resolved selection into the
166
+ // surface — the SAME selection a tree click yields.
167
+ expect(latestSurfaceSelectedFile()).toEqual({
168
+ entryId: "e1",
169
+ path: "src/main.go",
170
+ });
171
+ });
172
+
173
+ it("declines a platform path (returns false) and opens nothing", () => {
174
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
175
+
176
+ let handled: boolean | undefined;
177
+ act(() => {
178
+ handled = capturedOnFilePathClick()(".stigmer/skills/x.md");
179
+ });
180
+
181
+ expect(handled).toBe(false);
182
+ // The panel stays collapsed: no surface rendered.
183
+ expect(surfaceProps.length).toBe(0);
184
+ });
185
+
186
+ it("collapses back to full-width chat via the chip without losing the editor group", () => {
187
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
188
+ act(() => {
189
+ capturedOnFilePathClick()("/home/daytona/workspace/src/main.go");
190
+ });
191
+ expect(latestSurfaceSelectedFile()).toEqual({ entryId: "e1", path: "src/main.go" });
192
+
193
+ // Chip is the hide affordance while open …
194
+ fireEvent.click(screen.getByRole("button", { name: "Hide panel" }));
195
+ const countBefore = surfaceProps.length;
196
+
197
+ // … and the show affordance while collapsed; the editor group survives.
198
+ fireEvent.click(screen.getByRole("button", { name: "Show panel" }));
199
+ expect(surfaceProps.length).toBeGreaterThan(countBefore);
200
+ expect(latestSurfaceSelectedFile()).toEqual({
201
+ entryId: "e1",
202
+ path: "src/main.go",
203
+ });
204
+ });
205
+ });
@@ -1,12 +1,15 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { render, screen, cleanup } from "@testing-library/react";
2
+ import { render, screen, cleanup, fireEvent } from "@testing-library/react";
3
3
 
4
4
  // ---------------------------------------------------------------------------
5
- // Wiring-contract tests for the `audience` preset on the session organisms.
5
+ // Wiring-contract tests for the `audience` preset and the host slots on the
6
+ // session organisms.
6
7
  //
7
- // The composer and inspector are replaced with prop-capturing probes so the
8
- // tests assert exactly what `audience="endUser"` maps to: a locked agent,
9
- // unwired MCP/skill/session-variable pickers, and a read-only Setup tab.
8
+ // The composer and the Config facet (SetupTab) are replaced with
9
+ // prop-capturing probes so the tests assert exactly what `audience="endUser"`
10
+ // maps to: a locked agent, unwired MCP/skill/session-variable pickers, and a
11
+ // read-only Config facet. The Config facet lives in the session panel's rail,
12
+ // so the probe is reached by opening the panel (chip) and picking Config.
10
13
  // The molecules' own behavior is covered by their co-located tests.
11
14
  // ---------------------------------------------------------------------------
12
15
 
@@ -24,11 +27,11 @@ vi.mock("../../composer", async (importOriginal) => {
24
27
  };
25
28
  });
26
29
 
27
- const inspectorProps: CapturedProps[] = [];
28
- vi.mock("../inspector/SessionInspector", () => ({
29
- SessionInspector: (props: CapturedProps) => {
30
- inspectorProps.push(props);
31
- return <div data-testid="inspector-probe" />;
30
+ const setupTabProps: CapturedProps[] = [];
31
+ vi.mock("../facets/SetupTab", () => ({
32
+ SetupTab: (props: CapturedProps) => {
33
+ setupTabProps.push(props);
34
+ return <div data-testid="setup-probe" />;
32
35
  },
33
36
  }));
34
37
 
@@ -137,6 +140,15 @@ vi.mock("../useSessionPageFlow", () => ({
137
140
  useSessionPageFlow: (options: unknown) => mockUseSessionPageFlow(options),
138
141
  }));
139
142
 
143
+ vi.mock("../../hooks", () => ({
144
+ useStigmer: () => ({
145
+ agentExecution: {
146
+ uploadAttachment: vi.fn(),
147
+ getArtifactContent: vi.fn(),
148
+ },
149
+ }),
150
+ }));
151
+
140
152
  import { NewSessionViewer } from "../NewSessionViewer";
141
153
  import { SessionViewer } from "../SessionViewer";
142
154
 
@@ -147,14 +159,21 @@ function lastComposerProps(): CapturedProps {
147
159
  return composerProps.at(-1)!;
148
160
  }
149
161
 
150
- function lastSessionConfig(): { mutations?: unknown } {
151
- expect(inspectorProps.length).toBeGreaterThan(0);
152
- return inspectorProps.at(-1)!.sessionConfig as { mutations?: unknown };
162
+ /**
163
+ * Reach the Config facet through the unified panel: expand it via the
164
+ * top-right chip, pick Config in the rail, and return the props the (mocked)
165
+ * SetupTab received.
166
+ */
167
+ function openedConfigFacet(): { mutations?: unknown } {
168
+ fireEvent.click(screen.getByRole("button", { name: "Show panel" }));
169
+ fireEvent.click(screen.getByRole("radio", { name: "Config" }));
170
+ expect(setupTabProps.length).toBeGreaterThan(0);
171
+ return setupTabProps.at(-1)!;
153
172
  }
154
173
 
155
174
  beforeEach(() => {
156
175
  composerProps.length = 0;
157
- inspectorProps.length = 0;
176
+ setupTabProps.length = 0;
158
177
  stubSessionPageFlow.submitError = null;
159
178
  });
160
179
 
@@ -178,7 +197,7 @@ describe("NewSessionViewer — audience wiring", () => {
178
197
  expect(props.onMcpServerUsagesChange).toBeDefined();
179
198
  expect(props.onSkillRefsChange).toBeDefined();
180
199
  expect(props.sessionVariables).toBeDefined();
181
- expect(lastSessionConfig().mutations).toBeDefined();
200
+ expect(openedConfigFacet().mutations).toBeDefined();
182
201
  });
183
202
 
184
203
  it("endUser with a pinned agent: locked agent, integrator pickers unwired", () => {
@@ -199,8 +218,8 @@ describe("NewSessionViewer — audience wiring", () => {
199
218
  // End-user controls survive the curation.
200
219
  expect(props.showHarnessSelector).toBe(true);
201
220
  expect(props.showInteractionModePicker).toBe(true);
202
- // Setup tab is read-only: no remove affordances for pinned config.
203
- expect(lastSessionConfig().mutations).toBeUndefined();
221
+ // Config facet is read-only: no remove affordances for pinned config.
222
+ expect(openedConfigFacet().mutations).toBeUndefined();
204
223
  });
205
224
 
206
225
  it("endUser without a pinned agent: pickers hidden but agent not locked", () => {
@@ -241,7 +260,7 @@ describe("SessionViewer — audience wiring", () => {
241
260
  expect(props.onMcpServerUsagesChange).toBeDefined();
242
261
  expect(props.onSkillRefsChange).toBeDefined();
243
262
  expect(props.sessionVariables).toBeDefined();
244
- expect(lastSessionConfig().mutations).toBeDefined();
263
+ expect(openedConfigFacet().mutations).toBeDefined();
245
264
  });
246
265
 
247
266
  it("endUser: locked agent, integrator pickers unwired, read-only Setup tab", () => {
@@ -254,7 +273,7 @@ describe("SessionViewer — audience wiring", () => {
254
273
  expect(props.sessionVariables).toBeUndefined();
255
274
  // End-user controls survive the curation.
256
275
  expect(props.showInteractionModePicker).toBe(true);
257
- expect(lastSessionConfig().mutations).toBeUndefined();
276
+ expect(openedConfigFacet().mutations).toBeUndefined();
258
277
  });
259
278
 
260
279
  it("forwards getRuntimeEnv to the flow", () => {
@@ -268,6 +287,23 @@ describe("SessionViewer — audience wiring", () => {
268
287
  );
269
288
  });
270
289
 
290
+ it("threads accessSlot into the Config facet, not the header", () => {
291
+ render(
292
+ <SessionViewer
293
+ sessionId="ses_1"
294
+ org="acme"
295
+ accessSlot={<button type="button">Manage access</button>}
296
+ />,
297
+ );
298
+
299
+ // Collapsed panel: the slot must not surface anywhere (the header carries
300
+ // only headerActions and the chip).
301
+ expect(screen.queryByRole("button", { name: "Manage access" })).toBeNull();
302
+
303
+ const facet = openedConfigFacet() as { accessSlot?: unknown };
304
+ expect(facet.accessSlot).toBeDefined();
305
+ });
306
+
271
307
  it("renders the flow's submitError through the send-error banner", () => {
272
308
  stubSessionPageFlow.submitError = new Error("token mint failed");
273
309
  render(<SessionViewer sessionId="ses_1" org="acme" />);
@@ -0,0 +1,82 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { renderHook } from "@testing-library/react";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import {
5
+ FileChangeSchema,
6
+ type FileChange,
7
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
8
+ import { FileChangeType } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
9
+ import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // useOpenFileChange correlates the open file with the ONE session change that
13
+ // touched it (diff-as-default source, DD-06) — the contract that used to live on
14
+ // the inspector's Viewer tab and now feeds the workspace surface. The fold
15
+ // (useSessionFileChanges) is mocked; the join (findChangeForSelection) is real.
16
+ // ---------------------------------------------------------------------------
17
+
18
+ let mockFileChanges: FileChange[] = [];
19
+ vi.mock("../useSessionFileChanges", () => ({
20
+ useSessionFileChanges: (executions: readonly unknown[]) => ({
21
+ // Honor the EMPTY_EXECUTIONS gate: no executions in → no changes out.
22
+ fileChanges: executions.length > 0 ? mockFileChanges : [],
23
+ hasFileChanges: executions.length > 0 && mockFileChanges.length > 0,
24
+ fileChangeCount: executions.length > 0 ? mockFileChanges.length : 0,
25
+ }),
26
+ }));
27
+
28
+ import { useOpenFileChange } from "../useOpenFileChange";
29
+
30
+ const gitEntry = {
31
+ id: "e1",
32
+ name: "acme/app",
33
+ type: "git" as const,
34
+ gitUrl: "https://github.com/acme/app.git",
35
+ gitBranch: "main",
36
+ };
37
+ const entries = [gitEntry] as never;
38
+ const allExecutions = [{} as AgentExecution];
39
+ const root = "/home/daytona/workspace";
40
+
41
+ function modifyChange(path: string): FileChange {
42
+ return create(FileChangeSchema, { path, changeType: FileChangeType.MODIFY });
43
+ }
44
+
45
+ beforeEach(() => {
46
+ mockFileChanges = [];
47
+ });
48
+ afterEach(() => vi.clearAllMocks());
49
+
50
+ describe("useOpenFileChange", () => {
51
+ it("returns the matching change for the open file", () => {
52
+ mockFileChanges = [modifyChange("src/a.ts")];
53
+ const { result } = renderHook(() =>
54
+ useOpenFileChange({ entryId: "e1", path: "src/a.ts" }, allExecutions, entries, root),
55
+ );
56
+ expect(result.current).toBe(mockFileChanges[0]);
57
+ });
58
+
59
+ it("returns null when the open file was not changed this session", () => {
60
+ mockFileChanges = [modifyChange("src/a.ts")];
61
+ const { result } = renderHook(() =>
62
+ useOpenFileChange({ entryId: "e1", path: "src/other.ts" }, allExecutions, entries, root),
63
+ );
64
+ expect(result.current).toBeNull();
65
+ });
66
+
67
+ it("returns null when no file is open (fold is skipped)", () => {
68
+ mockFileChanges = [modifyChange("src/a.ts")];
69
+ const { result } = renderHook(() =>
70
+ useOpenFileChange(null, allExecutions, entries, root),
71
+ );
72
+ expect(result.current).toBeNull();
73
+ });
74
+
75
+ it("returns null when the session has no changes", () => {
76
+ mockFileChanges = [];
77
+ const { result } = renderHook(() =>
78
+ useOpenFileChange({ entryId: "e1", path: "src/a.ts" }, allExecutions, entries, root),
79
+ );
80
+ expect(result.current).toBeNull();
81
+ });
82
+ });
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import { renderHook, act, cleanup } from "@testing-library/react";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
5
+ import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
6
+ import type { SessionPlan } from "../../library/detect-plan-artifact";
7
+ import { usePlanDraft, planDraftKey } from "../usePlanDraft";
8
+
9
+ afterEach(cleanup);
10
+
11
+ function plan(executionId: string, contentHash: string): SessionPlan {
12
+ return {
13
+ executionId,
14
+ artifact: create(ExecutionArtifactSchema, {
15
+ name: "plan.md",
16
+ kind: ExecutionArtifactKind.FILE,
17
+ storageKey: `artifacts/${executionId}/plan.md`,
18
+ contentHash,
19
+ }),
20
+ };
21
+ }
22
+
23
+ describe("planDraftKey", () => {
24
+ it("keys by execution AND content hash", () => {
25
+ expect(planDraftKey(plan("e1", "aaa"))).toBe("e1:aaa");
26
+ expect(planDraftKey(plan("e1", "bbb"))).not.toBe(planDraftKey(plan("e1", "aaa")));
27
+ expect(planDraftKey(plan("e2", "aaa"))).not.toBe(planDraftKey(plan("e1", "aaa")));
28
+ });
29
+ });
30
+
31
+ describe("usePlanDraft", () => {
32
+ it("starts unedited and holds a draft for the current plan", () => {
33
+ const { result } = renderHook(({ p }) => usePlanDraft(p), {
34
+ initialProps: { p: plan("e1", "aaa") as SessionPlan | undefined },
35
+ });
36
+
37
+ expect(result.current.isEdited).toBe(false);
38
+ expect(result.current.draftText).toBeNull();
39
+
40
+ act(() => result.current.setDraft("# Edited plan"));
41
+ expect(result.current.isEdited).toBe(true);
42
+ expect(result.current.draftText).toBe("# Edited plan");
43
+ expect(result.current.readDraft()).toBe("# Edited plan");
44
+ });
45
+
46
+ it("reverts to the published plan when the draft is cleared", () => {
47
+ const { result } = renderHook(({ p }) => usePlanDraft(p), {
48
+ initialProps: { p: plan("e1", "aaa") as SessionPlan | undefined },
49
+ });
50
+
51
+ act(() => result.current.setDraft("edit"));
52
+ act(() => result.current.setDraft(null));
53
+ expect(result.current.isEdited).toBe(false);
54
+ expect(result.current.readDraft()).toBeNull();
55
+ });
56
+
57
+ it("drops the draft when a newer plan supersedes it (new execution)", () => {
58
+ const { result, rerender } = renderHook(({ p }) => usePlanDraft(p), {
59
+ initialProps: { p: plan("e1", "aaa") as SessionPlan | undefined },
60
+ });
61
+
62
+ act(() => result.current.setDraft("draft of plan 1"));
63
+ rerender({ p: plan("e2", "bbb") });
64
+
65
+ expect(result.current.isEdited).toBe(false);
66
+ expect(result.current.draftText).toBeNull();
67
+ expect(result.current.readDraft()).toBeNull();
68
+ });
69
+
70
+ it("drops the draft when the same plan is republished with new content", () => {
71
+ const { result, rerender } = renderHook(({ p }) => usePlanDraft(p), {
72
+ initialProps: { p: plan("e1", "aaa") as SessionPlan | undefined },
73
+ });
74
+
75
+ act(() => result.current.setDraft("stale draft"));
76
+ rerender({ p: plan("e1", "bbb") });
77
+
78
+ expect(result.current.readDraft()).toBeNull();
79
+ });
80
+
81
+ it("no-ops setDraft when the session has no plan", () => {
82
+ const { result } = renderHook(({ p }) => usePlanDraft(p), {
83
+ initialProps: { p: undefined as SessionPlan | undefined },
84
+ });
85
+
86
+ act(() => result.current.setDraft("nowhere to go"));
87
+ expect(result.current.isEdited).toBe(false);
88
+ expect(result.current.readDraft()).toBeNull();
89
+ });
90
+
91
+ it("keeps readDraft referentially stable across draft edits (DD-010)", () => {
92
+ const { result, rerender } = renderHook(({ p }) => usePlanDraft(p), {
93
+ initialProps: { p: plan("e1", "aaa") as SessionPlan | undefined },
94
+ });
95
+
96
+ const readBefore = result.current.readDraft;
97
+ act(() => result.current.setDraft("edit 1"));
98
+ rerender({ p: plan("e1", "aaa") });
99
+ expect(result.current.readDraft).toBe(readBefore);
100
+ expect(result.current.readDraft()).toBe("edit 1");
101
+ });
102
+ });
@@ -312,6 +312,50 @@ describe("useSessionConversation", () => {
312
312
  });
313
313
  });
314
314
 
315
+ it("sets NO optimistic pendingUserMessage for a buildFromPlan send (the thread hides the turn)", async () => {
316
+ methods.listBySession.mockResolvedValue({ entries: [] });
317
+ const newExec = makeExecution("new-exec", ExecutionPhase.EXECUTION_PENDING);
318
+ newExec.metadata!.id = "new-exec";
319
+ methods.executionCreate.mockResolvedValue(newExec);
320
+
321
+ const { result } = renderHook(
322
+ () => useSessionConversation("session-1", "org"),
323
+ { wrapper: createWrapper(mockStigmer) },
324
+ );
325
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
326
+
327
+ await act(async () => {
328
+ await result.current.sendFollowUp("Build from plan", {
329
+ buildFromPlan: true,
330
+ });
331
+ });
332
+
333
+ expect(result.current.pendingUserMessage).toBeNull();
334
+ expect(methods.executionCreate).toHaveBeenCalledWith(
335
+ expect.objectContaining({ message: "Build from plan" }),
336
+ );
337
+ });
338
+
339
+ it("sets pendingUserMessage when a buildFromPlan send FAILS (failure must be visible)", async () => {
340
+ methods.listBySession.mockResolvedValue({ entries: [] });
341
+ methods.executionCreate.mockRejectedValue(new Error("create boom"));
342
+
343
+ const { result } = renderHook(
344
+ () => useSessionConversation("session-1", "org"),
345
+ { wrapper: createWrapper(mockStigmer) },
346
+ );
347
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
348
+
349
+ await act(async () => {
350
+ await result.current.sendFollowUp("Build from plan", {
351
+ buildFromPlan: true,
352
+ });
353
+ });
354
+
355
+ expect(result.current.sendError?.message).toBe("create boom");
356
+ expect(result.current.pendingUserMessage).toBe("Build from plan");
357
+ });
358
+
315
359
  it("preserves the message and surfaces sendError when the send fails", async () => {
316
360
  methods.listBySession.mockResolvedValue({ entries: [] });
317
361
  methods.executionCreate.mockRejectedValue(new Error("create boom"));