@stigmer/react 3.1.1 → 3.1.2

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 (426) hide show
  1. package/composer/SessionComposer.d.ts +24 -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/FilePathContext.d.ts +8 -1
  10. package/execution/FilePathContext.d.ts.map +1 -1
  11. package/execution/FilePathContext.js.map +1 -1
  12. package/execution/FilePathLink.d.ts.map +1 -1
  13. package/execution/FilePathLink.js +21 -6
  14. package/execution/FilePathLink.js.map +1 -1
  15. package/execution/MessageEntry.d.ts +29 -0
  16. package/execution/MessageEntry.d.ts.map +1 -1
  17. package/execution/MessageEntry.js +29 -5
  18. package/execution/MessageEntry.js.map +1 -1
  19. package/execution/MessageThread.d.ts +76 -13
  20. package/execution/MessageThread.d.ts.map +1 -1
  21. package/execution/MessageThread.js +178 -24
  22. package/execution/MessageThread.js.map +1 -1
  23. package/execution/PlanArtifactCard.d.ts +27 -18
  24. package/execution/PlanArtifactCard.d.ts.map +1 -1
  25. package/execution/PlanArtifactCard.js +13 -23
  26. package/execution/PlanArtifactCard.js.map +1 -1
  27. package/execution/PlanDocumentMessage.d.ts +32 -0
  28. package/execution/PlanDocumentMessage.d.ts.map +1 -0
  29. package/execution/PlanDocumentMessage.js +37 -0
  30. package/execution/PlanDocumentMessage.js.map +1 -0
  31. package/execution/PlanStreamingCard.d.ts +45 -0
  32. package/execution/PlanStreamingCard.d.ts.map +1 -0
  33. package/execution/PlanStreamingCard.js +35 -0
  34. package/execution/PlanStreamingCard.js.map +1 -0
  35. package/execution/index.d.ts +5 -1
  36. package/execution/index.d.ts.map +1 -1
  37. package/execution/index.js +2 -0
  38. package/execution/index.js.map +1 -1
  39. package/execution/useCreateAgentExecution.d.ts +10 -0
  40. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  41. package/execution/useCreateAgentExecution.js +5 -1
  42. package/execution/useCreateAgentExecution.js.map +1 -1
  43. package/github/decodeGitHubContent.d.ts +29 -0
  44. package/github/decodeGitHubContent.d.ts.map +1 -0
  45. package/github/decodeGitHubContent.js +72 -0
  46. package/github/decodeGitHubContent.js.map +1 -0
  47. package/github/index.d.ts +1 -0
  48. package/github/index.d.ts.map +1 -1
  49. package/github/index.js +1 -0
  50. package/github/index.js.map +1 -1
  51. package/github/useGitHubFileReader.d.ts +26 -0
  52. package/github/useGitHubFileReader.d.ts.map +1 -0
  53. package/github/useGitHubFileReader.js +106 -0
  54. package/github/useGitHubFileReader.js.map +1 -0
  55. package/github/useGitHubTreeLister.d.ts.map +1 -1
  56. package/github/useGitHubTreeLister.js +6 -2
  57. package/github/useGitHubTreeLister.js.map +1 -1
  58. package/index.d.ts +9 -9
  59. package/index.d.ts.map +1 -1
  60. package/index.js +5 -5
  61. package/index.js.map +1 -1
  62. package/internal/ResizableSplit.d.ts +72 -22
  63. package/internal/ResizableSplit.d.ts.map +1 -1
  64. package/internal/ResizableSplit.js +96 -38
  65. package/internal/ResizableSplit.js.map +1 -1
  66. package/internal/VirtualizedThread.d.ts +5 -3
  67. package/internal/VirtualizedThread.d.ts.map +1 -1
  68. package/internal/VirtualizedThread.js +6 -4
  69. package/internal/VirtualizedThread.js.map +1 -1
  70. package/internal/file-icons/index.d.ts +17 -0
  71. package/internal/file-icons/index.d.ts.map +1 -0
  72. package/internal/file-icons/index.js +60 -0
  73. package/internal/file-icons/index.js.map +1 -0
  74. package/internal/file-tree/FileTreeNode.d.ts +18 -1
  75. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  76. package/internal/file-tree/FileTreeNode.js +11 -2
  77. package/internal/file-tree/FileTreeNode.js.map +1 -1
  78. package/internal/markdown-components.d.ts +38 -1
  79. package/internal/markdown-components.d.ts.map +1 -1
  80. package/internal/markdown-components.js +86 -11
  81. package/internal/markdown-components.js.map +1 -1
  82. package/{workflow → internal}/motion-preference.d.ts +7 -4
  83. package/internal/motion-preference.d.ts.map +1 -0
  84. package/{workflow → internal}/motion-preference.js +7 -4
  85. package/internal/motion-preference.js.map +1 -0
  86. package/internal/store/index.d.ts +33 -0
  87. package/internal/store/index.d.ts.map +1 -1
  88. package/internal/store/index.js +59 -0
  89. package/internal/store/index.js.map +1 -1
  90. package/internal/store/workspace-editors-store.d.ts +145 -0
  91. package/internal/store/workspace-editors-store.d.ts.map +1 -0
  92. package/internal/store/workspace-editors-store.js +208 -0
  93. package/internal/store/workspace-editors-store.js.map +1 -0
  94. package/internal/store/workspace-file-selection-store.d.ts +38 -0
  95. package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
  96. package/internal/store/workspace-file-selection-store.js +62 -0
  97. package/internal/store/workspace-file-selection-store.js.map +1 -0
  98. package/internal/useRevealLine.d.ts +49 -0
  99. package/internal/useRevealLine.d.ts.map +1 -0
  100. package/internal/useRevealLine.js +43 -0
  101. package/internal/useRevealLine.js.map +1 -0
  102. package/library/detect-plan-artifact.d.ts +21 -0
  103. package/library/detect-plan-artifact.d.ts.map +1 -1
  104. package/library/detect-plan-artifact.js +20 -0
  105. package/library/detect-plan-artifact.js.map +1 -1
  106. package/library/detect-streaming-plan.d.ts +45 -0
  107. package/library/detect-streaming-plan.d.ts.map +1 -0
  108. package/library/detect-streaming-plan.js +83 -0
  109. package/library/detect-streaming-plan.js.map +1 -0
  110. package/library/index.d.ts +4 -1
  111. package/library/index.d.ts.map +1 -1
  112. package/library/index.js +2 -1
  113. package/library/index.js.map +1 -1
  114. package/package.json +4 -4
  115. package/session/NewSessionViewer.d.ts +19 -4
  116. package/session/NewSessionViewer.d.ts.map +1 -1
  117. package/session/NewSessionViewer.js +52 -25
  118. package/session/NewSessionViewer.js.map +1 -1
  119. package/session/PlanEditor.d.ts +55 -0
  120. package/session/PlanEditor.d.ts.map +1 -0
  121. package/session/PlanEditor.js +88 -0
  122. package/session/PlanEditor.js.map +1 -0
  123. package/session/PlanStreamingDocument.d.ts +30 -0
  124. package/session/PlanStreamingDocument.d.ts.map +1 -0
  125. package/session/PlanStreamingDocument.js +37 -0
  126. package/session/PlanStreamingDocument.js.map +1 -0
  127. package/session/SessionPanelChip.d.ts +26 -0
  128. package/session/SessionPanelChip.d.ts.map +1 -0
  129. package/session/SessionPanelChip.js +40 -0
  130. package/session/SessionPanelChip.js.map +1 -0
  131. package/session/SessionViewer.d.ts +34 -7
  132. package/session/SessionViewer.d.ts.map +1 -1
  133. package/session/SessionViewer.js +315 -33
  134. package/session/SessionViewer.js.map +1 -1
  135. package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
  136. package/session/facets/ArtifactsTab.d.ts.map +1 -0
  137. package/session/{inspector → facets}/ArtifactsTab.js +8 -3
  138. package/session/facets/ArtifactsTab.js.map +1 -0
  139. package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
  140. package/session/facets/ChangesTab.d.ts.map +1 -0
  141. package/session/{inspector → facets}/ChangesTab.js +3 -3
  142. package/session/facets/ChangesTab.js.map +1 -0
  143. package/session/facets/InspectTab.d.ts.map +1 -0
  144. package/session/facets/InspectTab.js.map +1 -0
  145. package/session/{inspector → facets}/SetupTab.d.ts +11 -2
  146. package/session/facets/SetupTab.d.ts.map +1 -0
  147. package/session/{inspector → facets}/SetupTab.js +4 -3
  148. package/session/facets/SetupTab.js.map +1 -0
  149. package/session/{inspector → facets}/UsageTab.d.ts +1 -1
  150. package/session/facets/UsageTab.d.ts.map +1 -0
  151. package/session/{inspector → facets}/UsageTab.js +1 -1
  152. package/session/facets/UsageTab.js.map +1 -0
  153. package/session/{inspector → facets}/index.d.ts +0 -6
  154. package/session/facets/index.d.ts.map +1 -0
  155. package/session/{inspector → facets}/index.js +0 -3
  156. package/session/facets/index.js.map +1 -0
  157. package/session/index.d.ts +11 -2
  158. package/session/index.d.ts.map +1 -1
  159. package/session/index.js +15 -2
  160. package/session/index.js.map +1 -1
  161. package/session/plan-document.d.ts +9 -0
  162. package/session/plan-document.d.ts.map +1 -0
  163. package/session/plan-document.js +17 -0
  164. package/session/plan-document.js.map +1 -0
  165. package/session/useOpenFileChange.d.ts +20 -0
  166. package/session/useOpenFileChange.d.ts.map +1 -0
  167. package/session/useOpenFileChange.js +31 -0
  168. package/session/useOpenFileChange.js.map +1 -0
  169. package/session/usePlanDraft.d.ts +52 -0
  170. package/session/usePlanDraft.d.ts.map +1 -0
  171. package/session/usePlanDraft.js +63 -0
  172. package/session/usePlanDraft.js.map +1 -0
  173. package/session/useSessionConversation.d.ts +6 -0
  174. package/session/useSessionConversation.d.ts.map +1 -1
  175. package/session/useSessionConversation.js +1 -0
  176. package/session/useSessionConversation.js.map +1 -1
  177. package/session/useSessionPageFlow.d.ts.map +1 -1
  178. package/session/useSessionPageFlow.js +1 -0
  179. package/session/useSessionPageFlow.js.map +1 -1
  180. package/session/useSessionPanel.d.ts +120 -0
  181. package/session/useSessionPanel.d.ts.map +1 -0
  182. package/session/useSessionPanel.js +130 -0
  183. package/session/useSessionPanel.js.map +1 -0
  184. package/session/useSessionRailViews.d.ts +45 -0
  185. package/session/useSessionRailViews.d.ts.map +1 -0
  186. package/session/useSessionRailViews.js +105 -0
  187. package/session/useSessionRailViews.js.map +1 -0
  188. package/src/composer/SessionComposer.tsx +52 -10
  189. package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
  190. package/src/execution/ArtifactContentRenderer.tsx +101 -24
  191. package/src/execution/FilePathContext.tsx +8 -1
  192. package/src/execution/FilePathLink.tsx +22 -6
  193. package/src/execution/MessageEntry.tsx +111 -1
  194. package/src/execution/MessageThread.tsx +291 -36
  195. package/src/execution/PlanArtifactCard.tsx +63 -31
  196. package/src/execution/PlanDocumentMessage.tsx +96 -0
  197. package/src/execution/PlanStreamingCard.tsx +137 -0
  198. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
  199. package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
  200. package/src/execution/__tests__/MessageThread.test.tsx +101 -0
  201. package/src/execution/__tests__/PlanArtifactCard.test.tsx +66 -12
  202. package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
  203. package/src/execution/__tests__/PlanStreamingCard.test.tsx +49 -0
  204. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +504 -22
  205. package/src/execution/__tests__/message-entry.test.tsx +48 -0
  206. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
  207. package/src/execution/index.ts +7 -1
  208. package/src/execution/useCreateAgentExecution.ts +16 -1
  209. package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
  210. package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
  211. package/src/github/decodeGitHubContent.ts +83 -0
  212. package/src/github/index.ts +1 -0
  213. package/src/github/useGitHubFileReader.ts +152 -0
  214. package/src/github/useGitHubTreeLister.ts +6 -2
  215. package/src/index.ts +63 -9
  216. package/src/internal/ResizableSplit.tsx +184 -51
  217. package/src/internal/VirtualizedThread.tsx +12 -4
  218. package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
  219. package/src/internal/__tests__/markdown-components.test.tsx +68 -0
  220. package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
  221. package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
  222. package/src/internal/file-icons/index.tsx +137 -0
  223. package/src/internal/file-tree/FileTreeNode.tsx +43 -3
  224. package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
  225. package/src/internal/markdown-components.tsx +116 -11
  226. package/src/{workflow → internal}/motion-preference.ts +7 -4
  227. package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
  228. package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
  229. package/src/internal/store/index.ts +94 -0
  230. package/src/internal/store/workspace-editors-store.ts +315 -0
  231. package/src/internal/store/workspace-file-selection-store.ts +88 -0
  232. package/src/internal/useRevealLine.ts +81 -0
  233. package/src/library/__tests__/detect-plan-artifact.test.ts +48 -0
  234. package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
  235. package/src/library/detect-plan-artifact.ts +35 -0
  236. package/src/library/detect-streaming-plan.ts +108 -0
  237. package/src/library/index.ts +5 -0
  238. package/src/session/NewSessionViewer.tsx +123 -55
  239. package/src/session/PlanEditor.tsx +361 -0
  240. package/src/session/PlanStreamingDocument.tsx +97 -0
  241. package/src/session/SessionPanelChip.tsx +103 -0
  242. package/src/session/SessionViewer.tsx +617 -90
  243. package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
  244. package/src/session/__tests__/PlanEditor.test.tsx +217 -0
  245. package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
  246. package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
  247. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +9 -4
  248. package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
  249. package/src/session/__tests__/audienceWiring.test.tsx +55 -19
  250. package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
  251. package/src/session/__tests__/usePlanDraft.test.ts +102 -0
  252. package/src/session/__tests__/useSessionPanel.test.ts +298 -0
  253. package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
  254. package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
  255. package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
  256. package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
  257. package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
  258. package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +35 -1
  259. package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
  260. package/src/session/{inspector → facets}/index.ts +0 -16
  261. package/src/session/index.ts +30 -13
  262. package/src/session/plan-document.ts +19 -0
  263. package/src/session/useOpenFileChange.ts +53 -0
  264. package/src/session/usePlanDraft.ts +109 -0
  265. package/src/session/useSessionConversation.ts +7 -0
  266. package/src/session/useSessionPageFlow.ts +1 -0
  267. package/src/session/useSessionPanel.ts +288 -0
  268. package/src/session/useSessionRailViews.tsx +200 -0
  269. package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
  270. package/src/workflow/index.ts +1 -1
  271. package/src/workflow/useFollowExecution.ts +1 -1
  272. package/src/workspace/EditorTabs.tsx +145 -0
  273. package/src/workspace/ExplorerTree.tsx +247 -0
  274. package/src/workspace/FileViewer.tsx +516 -0
  275. package/src/workspace/WorkspaceContentSearch.tsx +478 -0
  276. package/src/workspace/WorkspaceContentSearcher.ts +70 -0
  277. package/src/workspace/WorkspaceFileLister.ts +11 -0
  278. package/src/workspace/WorkspaceFileReader.ts +57 -0
  279. package/src/workspace/WorkspaceFileSearch.tsx +429 -0
  280. package/src/workspace/WorkspaceSurface.tsx +781 -0
  281. package/src/workspace/__tests__/EditorTabs.test.tsx +100 -0
  282. package/src/workspace/__tests__/ExplorerTree.test.tsx +152 -0
  283. package/src/workspace/__tests__/FileViewer.test.tsx +421 -0
  284. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +196 -0
  285. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +206 -0
  286. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +348 -0
  287. package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
  288. package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
  289. package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
  290. package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
  291. package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
  292. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
  293. package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
  294. package/src/workspace/findChangeForSelection.ts +61 -0
  295. package/src/workspace/index.ts +56 -2
  296. package/src/workspace/matchContentPreview.ts +51 -0
  297. package/src/workspace/matchWorkspaceFiles.ts +86 -0
  298. package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
  299. package/src/workspace/useWorkspaceContentSearch.ts +228 -0
  300. package/src/workspace/useWorkspaceFileContent.ts +129 -0
  301. package/src/workspace/useWorkspaceFileSearch.ts +184 -0
  302. package/src/workspace/useWorkspaceFiles.ts +27 -27
  303. package/src/workspace/workspaceListingCache.ts +93 -0
  304. package/styles.css +1 -1
  305. package/workflow/WorkflowExecutionGraph.js +1 -1
  306. package/workflow/WorkflowExecutionGraph.js.map +1 -1
  307. package/workflow/index.d.ts +1 -1
  308. package/workflow/index.d.ts.map +1 -1
  309. package/workflow/index.js +1 -1
  310. package/workflow/index.js.map +1 -1
  311. package/workflow/useFollowExecution.js +1 -1
  312. package/workflow/useFollowExecution.js.map +1 -1
  313. package/workspace/EditorTabs.d.ts +29 -0
  314. package/workspace/EditorTabs.d.ts.map +1 -0
  315. package/workspace/EditorTabs.js +66 -0
  316. package/workspace/EditorTabs.js.map +1 -0
  317. package/workspace/ExplorerTree.d.ts +32 -0
  318. package/workspace/ExplorerTree.d.ts.map +1 -0
  319. package/workspace/ExplorerTree.js +45 -0
  320. package/workspace/ExplorerTree.js.map +1 -0
  321. package/workspace/FileViewer.d.ts +93 -0
  322. package/workspace/FileViewer.d.ts.map +1 -0
  323. package/workspace/FileViewer.js +182 -0
  324. package/workspace/FileViewer.js.map +1 -0
  325. package/workspace/WorkspaceContentSearch.d.ts +41 -0
  326. package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
  327. package/workspace/WorkspaceContentSearch.js +195 -0
  328. package/workspace/WorkspaceContentSearch.js.map +1 -0
  329. package/workspace/WorkspaceContentSearcher.d.ts +59 -0
  330. package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
  331. package/workspace/WorkspaceContentSearcher.js +7 -0
  332. package/workspace/WorkspaceContentSearcher.js.map +1 -0
  333. package/workspace/WorkspaceFileLister.d.ts +11 -0
  334. package/workspace/WorkspaceFileLister.d.ts.map +1 -1
  335. package/workspace/WorkspaceFileReader.d.ts +48 -0
  336. package/workspace/WorkspaceFileReader.d.ts.map +1 -0
  337. package/workspace/WorkspaceFileReader.js +11 -0
  338. package/workspace/WorkspaceFileReader.js.map +1 -0
  339. package/workspace/WorkspaceFileSearch.d.ts +32 -0
  340. package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
  341. package/workspace/WorkspaceFileSearch.js +155 -0
  342. package/workspace/WorkspaceFileSearch.js.map +1 -0
  343. package/workspace/WorkspaceSurface.d.ts +151 -0
  344. package/workspace/WorkspaceSurface.d.ts.map +1 -0
  345. package/workspace/WorkspaceSurface.js +184 -0
  346. package/workspace/WorkspaceSurface.js.map +1 -0
  347. package/workspace/findChangeForSelection.d.ts +19 -0
  348. package/workspace/findChangeForSelection.d.ts.map +1 -0
  349. package/workspace/findChangeForSelection.js +46 -0
  350. package/workspace/findChangeForSelection.js.map +1 -0
  351. package/workspace/index.d.ts +24 -2
  352. package/workspace/index.d.ts.map +1 -1
  353. package/workspace/index.js +13 -1
  354. package/workspace/index.js.map +1 -1
  355. package/workspace/matchContentPreview.d.ts +21 -0
  356. package/workspace/matchContentPreview.d.ts.map +1 -0
  357. package/workspace/matchContentPreview.js +36 -0
  358. package/workspace/matchContentPreview.js.map +1 -0
  359. package/workspace/matchWorkspaceFiles.d.ts +30 -0
  360. package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
  361. package/workspace/matchWorkspaceFiles.js +58 -0
  362. package/workspace/matchWorkspaceFiles.js.map +1 -0
  363. package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
  364. package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
  365. package/workspace/resolveWorkspaceFileSelection.js +115 -0
  366. package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
  367. package/workspace/useWorkspaceContentSearch.d.ts +69 -0
  368. package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
  369. package/workspace/useWorkspaceContentSearch.js +154 -0
  370. package/workspace/useWorkspaceContentSearch.js.map +1 -0
  371. package/workspace/useWorkspaceFileContent.d.ts +73 -0
  372. package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
  373. package/workspace/useWorkspaceFileContent.js +57 -0
  374. package/workspace/useWorkspaceFileContent.js.map +1 -0
  375. package/workspace/useWorkspaceFileSearch.d.ts +61 -0
  376. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
  377. package/workspace/useWorkspaceFileSearch.js +119 -0
  378. package/workspace/useWorkspaceFileSearch.js.map +1 -0
  379. package/workspace/useWorkspaceFiles.d.ts +13 -5
  380. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  381. package/workspace/useWorkspaceFiles.js +18 -19
  382. package/workspace/useWorkspaceFiles.js.map +1 -1
  383. package/workspace/workspaceListingCache.d.ts +47 -0
  384. package/workspace/workspaceListingCache.d.ts.map +1 -0
  385. package/workspace/workspaceListingCache.js +55 -0
  386. package/workspace/workspaceListingCache.js.map +1 -0
  387. package/session/inspector/ArtifactsTab.d.ts.map +0 -1
  388. package/session/inspector/ArtifactsTab.js.map +0 -1
  389. package/session/inspector/ChangesTab.d.ts.map +0 -1
  390. package/session/inspector/ChangesTab.js.map +0 -1
  391. package/session/inspector/InspectTab.d.ts.map +0 -1
  392. package/session/inspector/InspectTab.js.map +0 -1
  393. package/session/inspector/SessionInspector.d.ts +0 -64
  394. package/session/inspector/SessionInspector.d.ts.map +0 -1
  395. package/session/inspector/SessionInspector.js +0 -53
  396. package/session/inspector/SessionInspector.js.map +0 -1
  397. package/session/inspector/SetupTab.d.ts.map +0 -1
  398. package/session/inspector/SetupTab.js.map +0 -1
  399. package/session/inspector/UsageTab.d.ts.map +0 -1
  400. package/session/inspector/UsageTab.js.map +0 -1
  401. package/session/inspector/WorkspaceTab.d.ts +0 -35
  402. package/session/inspector/WorkspaceTab.d.ts.map +0 -1
  403. package/session/inspector/WorkspaceTab.js +0 -75
  404. package/session/inspector/WorkspaceTab.js.map +0 -1
  405. package/session/inspector/index.d.ts.map +0 -1
  406. package/session/inspector/index.js.map +0 -1
  407. package/session/inspector/useSessionInspector.d.ts +0 -49
  408. package/session/inspector/useSessionInspector.d.ts.map +0 -1
  409. package/session/inspector/useSessionInspector.js +0 -122
  410. package/session/inspector/useSessionInspector.js.map +0 -1
  411. package/src/session/inspector/SessionInspector.tsx +0 -162
  412. package/src/session/inspector/WorkspaceTab.tsx +0 -245
  413. package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
  414. package/src/session/inspector/useSessionInspector.ts +0 -178
  415. package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
  416. package/workflow/motion-preference.d.ts.map +0 -1
  417. package/workflow/motion-preference.js.map +0 -1
  418. package/workspace/WorkspaceEntryFiles.d.ts +0 -22
  419. package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
  420. package/workspace/WorkspaceEntryFiles.js +0 -58
  421. package/workspace/WorkspaceEntryFiles.js.map +0 -1
  422. /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
  423. /package/session/{inspector → facets}/InspectTab.js +0 -0
  424. /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
  425. /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
  426. /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
@@ -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
+ });
@@ -0,0 +1,298 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import { renderHook, act, cleanup } from "@testing-library/react";
3
+ import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
4
+ import { useSessionPanel, type UseSessionPanelOptions } from "../useSessionPanel";
5
+ import { PLAN_DOCUMENT_ENTRY_ID, PLAN_DOCUMENT_PATH } from "../plan-document";
6
+ import type { SelectedThreadItem } from "../../internal/store/selection-store";
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // The unified-panel controller: collapsed-by-default state, the open-editor
10
+ // group, and the rail-view FSM ported from the retired inspector tabs. The
11
+ // FSM rules under test: explicit picks are sticky; auto-switching happens only
12
+ // in an OPEN panel (arrivals/selections never auto-open); running⇄terminal
13
+ // transitions reset stickiness.
14
+ // ---------------------------------------------------------------------------
15
+
16
+ const item = { kind: "toolCall" } as unknown as SelectedThreadItem;
17
+
18
+ function renderPanel(initial?: Partial<UseSessionPanelOptions>) {
19
+ return renderHook(
20
+ (opts: UseSessionPanelOptions) => useSessionPanel(opts),
21
+ {
22
+ initialProps: {
23
+ phase: null,
24
+ hasChanges: false,
25
+ ...initial,
26
+ } as UseSessionPanelOptions,
27
+ },
28
+ );
29
+ }
30
+
31
+ afterEach(cleanup);
32
+
33
+ describe("useSessionPanel — open/collapse", () => {
34
+ it("starts collapsed on the Explorer view", () => {
35
+ const { result } = renderPanel();
36
+ expect(result.current.isOpen).toBe(false);
37
+ expect(result.current.view).toBe("files");
38
+ });
39
+
40
+ it("opens via openPanel and collapses via closePanel, preserving the view", () => {
41
+ const { result } = renderPanel();
42
+ act(() => result.current.openPanel());
43
+ expect(result.current.isOpen).toBe(true);
44
+ act(() => result.current.setView("usage"));
45
+ act(() => result.current.closePanel());
46
+ expect(result.current.isOpen).toBe(false);
47
+ act(() => result.current.openPanel());
48
+ expect(result.current.view).toBe("usage");
49
+ });
50
+
51
+ it("openFile opens the panel with a preview tab", () => {
52
+ const { result } = renderPanel();
53
+ act(() => result.current.openFile("e1", "src/main.go"));
54
+ expect(result.current.isOpen).toBe(true);
55
+ expect(result.current.editorsStore.getSnapshot().editors).toEqual([
56
+ { entryId: "e1", path: "src/main.go", preview: true },
57
+ ]);
58
+ });
59
+
60
+ it("keeps the panel open when the last editor closes (the panel is more than files)", () => {
61
+ const { result } = renderPanel();
62
+ act(() => result.current.openFile("e1", "a.ts"));
63
+ act(() => result.current.closeEditor("e1", "a.ts"));
64
+ expect(result.current.editorsStore.getSnapshot().editors).toEqual([]);
65
+ expect(result.current.isOpen).toBe(true);
66
+ });
67
+ });
68
+
69
+ describe("useSessionPanel — selection (Inspect)", () => {
70
+ it("auto-surfaces Inspect for a selection while the panel is open", () => {
71
+ const { result } = renderPanel();
72
+ act(() => result.current.openPanel());
73
+ act(() => result.current.notifySelection(item));
74
+ expect(result.current.view).toBe("inspect");
75
+ });
76
+
77
+ it("never auto-opens a collapsed panel on selection", () => {
78
+ const { result } = renderPanel();
79
+ act(() => result.current.notifySelection(item));
80
+ expect(result.current.isOpen).toBe(false);
81
+ expect(result.current.view).toBe("files");
82
+ });
83
+
84
+ it("surfaces a selection made while collapsed when the panel opens", () => {
85
+ const { result } = renderPanel();
86
+ act(() => result.current.notifySelection(item));
87
+ act(() => result.current.openPanel());
88
+ expect(result.current.view).toBe("inspect");
89
+ });
90
+
91
+ it("yields to an explicit pick (sticky) over selection auto-switch", () => {
92
+ const { result } = renderPanel();
93
+ act(() => result.current.openPanel());
94
+ act(() => result.current.setView("usage"));
95
+ act(() => result.current.notifySelection(item));
96
+ expect(result.current.view).toBe("usage");
97
+ });
98
+
99
+ it("leaves Inspect (and unsticks) when the selection clears", () => {
100
+ const { result } = renderPanel();
101
+ act(() => result.current.openPanel());
102
+ act(() => result.current.notifySelection(item));
103
+ expect(result.current.view).toBe("inspect");
104
+ act(() => result.current.notifySelection(null));
105
+ expect(result.current.view).toBe("files");
106
+ // Unstuck: the next selection auto-surfaces Inspect again.
107
+ act(() => result.current.notifySelection(item));
108
+ expect(result.current.view).toBe("inspect");
109
+ });
110
+ });
111
+
112
+ describe("useSessionPanel — arrivals and phase", () => {
113
+ it("auto-surfaces Changes on first write-back while the panel is open", () => {
114
+ const { result, rerender } = renderPanel();
115
+ act(() => result.current.openPanel());
116
+ rerender({ phase: null, hasChanges: true });
117
+ expect(result.current.view).toBe("changes");
118
+ });
119
+
120
+ it("never auto-opens (or re-routes) a collapsed panel on a write-back — badge only", () => {
121
+ const { result, rerender } = renderPanel();
122
+ rerender({ phase: null, hasChanges: true });
123
+ expect(result.current.isOpen).toBe(false);
124
+ expect(result.current.view).toBe("files");
125
+ });
126
+
127
+ it("respects a sticky pick when a write-back arrives", () => {
128
+ const { result, rerender } = renderPanel();
129
+ act(() => result.current.openPanel());
130
+ act(() => result.current.setView("usage"));
131
+ rerender({ phase: null, hasChanges: true });
132
+ expect(result.current.view).toBe("usage");
133
+ });
134
+
135
+ it("resets the sticky pick on a running→terminal transition", () => {
136
+ const { result, rerender } = renderPanel({
137
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
138
+ });
139
+ act(() => result.current.openPanel());
140
+ act(() => result.current.setView("usage"));
141
+ rerender({
142
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
143
+ hasChanges: false,
144
+ });
145
+ expect(result.current.view).toBe("files");
146
+ // Unstuck: a selection now auto-surfaces Inspect.
147
+ act(() => result.current.notifySelection(item));
148
+ expect(result.current.view).toBe("inspect");
149
+ });
150
+ });
151
+
152
+ describe("useSessionPanel — plan document tab", () => {
153
+ const planTab = {
154
+ entryId: PLAN_DOCUMENT_ENTRY_ID,
155
+ path: PLAN_DOCUMENT_PATH,
156
+ preview: false,
157
+ };
158
+
159
+ function planEditors(result: { current: ReturnType<typeof useSessionPanel> }) {
160
+ return result.current.editorsStore.getSnapshot().editors;
161
+ }
162
+
163
+ it("openPlanDocument opens the panel with a pinned plan tab", () => {
164
+ const { result } = renderPanel();
165
+ act(() => result.current.openPlanDocument());
166
+ expect(result.current.isOpen).toBe(true);
167
+ expect(planEditors(result)).toEqual([planTab]);
168
+ });
169
+
170
+ it("keeps the plan tab pinned across preview browsing (never evicted)", () => {
171
+ const { result } = renderPanel();
172
+ act(() => result.current.openPlanDocument());
173
+ // Preview browsing reuses the single preview slot; the pinned plan tab
174
+ // must survive it untouched.
175
+ act(() => result.current.openFile("e1", "a.ts"));
176
+ act(() => result.current.openFile("e1", "b.ts"));
177
+ expect(planEditors(result)).toEqual([
178
+ planTab,
179
+ { entryId: "e1", path: "b.ts", preview: true },
180
+ ]);
181
+ });
182
+
183
+ it("auto-opens the plan tab when a plan arrives — even from a collapsed panel", () => {
184
+ // The one deliberate exception to the "never open a collapsed panel"
185
+ // arrival convention: a completing plan is the turn's deliverable, and
186
+ // with the thread showing only a compact card there is no other review
187
+ // surface.
188
+ const { result, rerender } = renderPanel();
189
+ expect(result.current.isOpen).toBe(false);
190
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
191
+ expect(result.current.isOpen).toBe(true);
192
+ expect(planEditors(result)).toEqual([planTab]);
193
+ });
194
+
195
+ it("activates (focuses) the plan tab on arrival, over the open file", () => {
196
+ const { result, rerender } = renderPanel();
197
+ act(() => result.current.openFile("e1", "a.ts"));
198
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
199
+ const { activeFile } = result.current.editorsStore.getSnapshot();
200
+ expect(activeFile).toEqual({
201
+ entryId: PLAN_DOCUMENT_ENTRY_ID,
202
+ path: PLAN_DOCUMENT_PATH,
203
+ });
204
+ });
205
+
206
+ it("re-opens the plan tab when a refined plan supersedes the current one (new identity)", () => {
207
+ const { result, rerender } = renderPanel();
208
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
209
+ act(() =>
210
+ result.current.closeEditor(PLAN_DOCUMENT_ENTRY_ID, PLAN_DOCUMENT_PATH),
211
+ );
212
+ expect(planEditors(result)).toEqual([]);
213
+ rerender({ phase: null, hasChanges: false, planKey: "e2:hash-b" });
214
+ expect(planEditors(result)).toEqual([planTab]);
215
+ });
216
+
217
+ it("opens on the streaming identity, then idempotently re-fires when the plan publishes", () => {
218
+ // The viewer feeds `<executionId>:streaming` while the plan is being
219
+ // written, then the artifact identity once published — two identities,
220
+ // one pinned tab: the transition must not duplicate it or disturb it.
221
+ const { result, rerender } = renderPanel();
222
+ rerender({ phase: null, hasChanges: false, planKey: "e1:streaming" });
223
+ expect(result.current.isOpen).toBe(true);
224
+ expect(planEditors(result)).toEqual([planTab]);
225
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
226
+ expect(planEditors(result)).toEqual([planTab]);
227
+ });
228
+
229
+ it("does not re-open the plan tab for an unchanged identity", () => {
230
+ const { result, rerender } = renderPanel();
231
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
232
+ act(() =>
233
+ result.current.closeEditor(PLAN_DOCUMENT_ENTRY_ID, PLAN_DOCUMENT_PATH),
234
+ );
235
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
236
+ expect(planEditors(result)).toEqual([]);
237
+ });
238
+
239
+ it("does not open anything for a plan that existed at mount", () => {
240
+ // Loading a session with an old plan must not hijack the layout.
241
+ const { result, rerender } = renderPanel({ planKey: "e1:hash-a" });
242
+ expect(result.current.isOpen).toBe(false);
243
+ expect(planEditors(result)).toEqual([]);
244
+ // …and the mount identity doesn't count as "new" on later rerenders.
245
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
246
+ expect(planEditors(result)).toEqual([]);
247
+ });
248
+
249
+ it("leaves the rail view alone (the tab lives in the editor area)", () => {
250
+ const { result, rerender } = renderPanel();
251
+ act(() => result.current.openPanel());
252
+ act(() => result.current.setView("usage"));
253
+ rerender({ phase: null, hasChanges: false, planKey: "e1:hash-a" });
254
+ expect(result.current.view).toBe("usage");
255
+ });
256
+ });
257
+
258
+ describe("useSessionPanel — defaultView (home view)", () => {
259
+ it("seeds the view to the provided defaultView (launcher homes on Config)", () => {
260
+ const { result } = renderPanel({ defaultView: "configure" });
261
+ expect(result.current.view).toBe("configure");
262
+ });
263
+
264
+ it("returns to defaultView (not files) on a running→terminal reset", () => {
265
+ const { result, rerender } = renderPanel({
266
+ defaultView: "configure",
267
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
268
+ });
269
+ act(() => result.current.openPanel());
270
+ act(() => result.current.setView("usage"));
271
+ rerender({
272
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
273
+ hasChanges: false,
274
+ defaultView: "configure",
275
+ });
276
+ expect(result.current.view).toBe("configure");
277
+ });
278
+
279
+ it("returns to defaultView (not files) when a selection clears", () => {
280
+ const { result } = renderPanel({ defaultView: "configure" });
281
+ act(() => result.current.openPanel());
282
+ act(() => result.current.notifySelection(item));
283
+ expect(result.current.view).toBe("inspect");
284
+ act(() => result.current.notifySelection(null));
285
+ expect(result.current.view).toBe("configure");
286
+ });
287
+
288
+ it("defaults the home view to Explorer (files) when omitted", () => {
289
+ const { result, rerender } = renderPanel({
290
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
291
+ });
292
+ expect(result.current.view).toBe("files");
293
+ act(() => result.current.openPanel());
294
+ act(() => result.current.setView("usage"));
295
+ rerender({ phase: ExecutionPhase.EXECUTION_COMPLETED, hasChanges: false });
296
+ expect(result.current.view).toBe("files");
297
+ });
298
+ });